1pub mod config;
2pub mod cpi;
3pub mod elf;
4pub mod errors;
5pub mod runtime;
6pub mod serialize;
7pub mod syscalls;
8
9pub use {
10 runtime::{ElfSource, ExecutionResult, LogCollector, Runtime},
11 sbpf_common::instruction::Instruction,
12 sbpf_vm::vm::CallFrame,
13};
14
15#[cfg(test)]
16mod tests {
17 use {
18 crate::{Runtime, config::RuntimeConfig},
19 mollusk_svm::{Mollusk, result::Check},
20 solana_account::Account,
21 solana_address::Address,
22 solana_instruction::{AccountMeta, Instruction},
23 solana_program_pack::Pack,
24 std::{error::Error, path::PathBuf},
25 };
26
27 pub const ESCROW_SEED: &[u8] = b"escrow";
28 pub const PROGRAM_ID: Address =
29 Address::from_str_const("22222222222222222222222222222222222222222222");
30
31 fn fixtures_dir() -> PathBuf {
32 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")
33 }
34
35 fn setup_mollusk() -> Mollusk {
36 let fixtures = fixtures_dir();
37 let escrow_elf_path = fixtures.join("libupstream_pinocchio_escrow");
38 let mut mollusk = Mollusk::new(&PROGRAM_ID, escrow_elf_path.to_str().unwrap());
39 mollusk_svm_programs_token::token::add_program(&mut mollusk);
40 mollusk_svm_programs_token::associated_token::add_program(&mut mollusk);
41 mollusk
42 }
43
44 fn setup_sbpf_runtime() -> Runtime {
45 let fixtures = fixtures_dir();
46 let escrow_elf_path = fixtures.join("libupstream_pinocchio_escrow.so");
47 let token_elf_path = fixtures.join("token.so");
48 let associated_token_elf_path = fixtures.join("associated_token.so");
49 let config = RuntimeConfig {
50 compute_budget: 1_400_000,
51 max_cpi_depth: 4,
52 ..RuntimeConfig::default()
53 };
54 let mut runtime =
55 Runtime::new(PROGRAM_ID, escrow_elf_path.to_str().unwrap(), config).unwrap();
56 runtime.add_program(&spl_token_interface::ID, token_elf_path.to_str().unwrap());
57 runtime.add_program(
58 &Address::from_str_const("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"),
59 associated_token_elf_path.to_str().unwrap(),
60 );
61 runtime
62 }
63
64 #[test]
65 pub fn make_and_take() -> Result<(), Box<dyn Error>> {
66 let mollusk = setup_mollusk();
67
68 let maker = Address::new_unique();
69 let taker = Address::new_unique();
70 let mint_a = Address::new_unique();
71 let mint_b = Address::new_unique();
72
73 let mint_a_data = spl_token_interface::state::Mint {
74 mint_authority: None.into(),
75 supply: 1_000_000_000_000_000,
76 decimals: 6,
77 is_initialized: true,
78 freeze_authority: None.into(),
79 };
80
81 let mint_b_data = spl_token_interface::state::Mint {
82 mint_authority: None.into(),
83 supply: 1_000_000_000_000_000,
84 decimals: 6,
85 is_initialized: true,
86 freeze_authority: None.into(),
87 };
88
89 let maker_token_a_data = spl_token_interface::state::Account {
90 mint: mint_a.to_bytes().into(),
91 owner: maker.to_bytes().into(),
92 amount: 100_000_000,
93 delegate: None.into(),
94 state: spl_token_interface::state::AccountState::Initialized,
95 is_native: None.into(),
96 delegated_amount: 0,
97 close_authority: None.into(),
98 };
99
100 let taker_token_b_data = spl_token_interface::state::Account {
101 mint: mint_b.to_bytes().into(),
102 owner: taker.to_bytes().into(),
103 amount: 100_000_000,
104 delegate: None.into(),
105 state: spl_token_interface::state::AccountState::Initialized,
106 is_native: None.into(),
107 delegated_amount: 0,
108 close_authority: None.into(),
109 };
110
111 let (maker_token_a, maker_token_a_account) =
112 mollusk_svm_programs_token::associated_token::create_account_for_associated_token_account(maker_token_a_data);
113 let (taker_token_b, taker_token_b_account) =
114 mollusk_svm_programs_token::associated_token::create_account_for_associated_token_account(taker_token_b_data);
115
116 let mut mint_a_data_bytes = vec![0u8; spl_token_interface::state::Mint::LEN];
117 let mut mint_b_data_bytes = vec![0u8; spl_token_interface::state::Mint::LEN];
118
119 mint_a_data.pack_into_slice(&mut mint_a_data_bytes);
120 mint_b_data.pack_into_slice(&mut mint_b_data_bytes);
121
122 let maker_account = Account::new(10_000_000_000, 0, &Address::default());
123 let taker_account = Account::new(10_000_000_000, 0, &Address::default());
124 let mint_a_account = Account {
125 lamports: mollusk
126 .sysvars
127 .rent
128 .minimum_balance(spl_token_interface::state::Mint::LEN),
129 data: mint_a_data_bytes,
130 owner: mollusk_svm_programs_token::token::ID,
131 executable: false,
132 rent_epoch: 0,
133 };
134
135 let mint_b_account = Account {
136 lamports: mollusk
137 .sysvars
138 .rent
139 .minimum_balance(spl_token_interface::state::Mint::LEN),
140 data: mint_b_data_bytes,
141 owner: mollusk_svm_programs_token::token::ID,
142 executable: false,
143 rent_epoch: 0,
144 };
145
146 let (system_program, system_program_account) =
147 mollusk_svm::program::keyed_account_for_system_program();
148 let (token_program, token_program_account) =
149 mollusk_svm_programs_token::token::keyed_account();
150 let (associated_token_program, associated_token_program_account) =
151 mollusk_svm_programs_token::associated_token::keyed_account();
152
153 let seeds: &[&[u8]] = &[
154 ESCROW_SEED.as_ref(),
155 maker.as_ref(),
156 mint_a.as_ref(),
157 mint_b.as_ref(),
158 ];
159 let escrow = Address::find_program_address(seeds, &PROGRAM_ID).0;
160 let escrow_account = Account::default();
161
162 let vault = spl_associated_token_account_interface::address::get_associated_token_address(
163 &escrow, &mint_a,
164 );
165 let vault_account = Account::default();
166
167 let maker_token_b =
168 spl_associated_token_account_interface::address::get_associated_token_address(
169 &maker, &mint_b,
170 );
171 let maker_token_b_account = Account::default();
172 let taker_token_a =
173 spl_associated_token_account_interface::address::get_associated_token_address(
174 &taker, &mint_a,
175 );
176 let taker_token_a_account = Account::default();
177
178 let make_instruction = &Instruction {
179 program_id: PROGRAM_ID,
180 accounts: vec![
181 AccountMeta::new(maker, true),
182 AccountMeta::new_readonly(mint_a, false),
183 AccountMeta::new_readonly(mint_b, false),
184 AccountMeta::new(maker_token_a, false),
185 AccountMeta::new(escrow, false),
186 AccountMeta::new(vault, false),
187 AccountMeta::new_readonly(token_program, false),
188 AccountMeta::new_readonly(associated_token_program, false),
189 AccountMeta::new_readonly(system_program, false),
190 ],
191 data: vec![0, 13, 37, 0, 0, 0, 0, 0, 0, 13, 37, 0, 0, 0, 0, 0, 0],
192 };
193
194 let take_instruction = &Instruction {
195 program_id: PROGRAM_ID,
196 accounts: vec![
197 AccountMeta::new(taker, true),
198 AccountMeta::new(maker, false),
199 AccountMeta::new_readonly(mint_a, false),
200 AccountMeta::new_readonly(mint_b, false),
201 AccountMeta::new(maker_token_b, false),
202 AccountMeta::new(taker_token_a, false),
203 AccountMeta::new(taker_token_b, false),
204 AccountMeta::new(escrow, false),
205 AccountMeta::new(vault, false),
206 AccountMeta::new_readonly(token_program, false),
207 AccountMeta::new_readonly(associated_token_program, false),
208 AccountMeta::new_readonly(system_program, false),
209 ],
210 data: vec![1],
211 };
212
213 let accounts = &vec![
214 (maker, maker_account.clone()),
215 (taker, taker_account.clone()),
216 (mint_a, mint_a_account.clone()),
217 (mint_b, mint_b_account.clone()),
218 (maker_token_a, maker_token_a_account.clone()),
219 (maker_token_b, maker_token_b_account.clone()),
220 (taker_token_a, taker_token_a_account.clone()),
221 (taker_token_b, taker_token_b_account.clone()),
222 (escrow, escrow_account.clone()),
223 (vault, vault_account.clone()),
224 (token_program, token_program_account.clone()),
225 (
226 associated_token_program,
227 associated_token_program_account.clone(),
228 ),
229 (system_program, system_program_account.clone()),
230 ];
231
232 let mut runtime = setup_sbpf_runtime();
234
235 let result = runtime.run(&make_instruction, &accounts)?;
237 assert_eq!(result.exit_code, Some(0));
239 let vault_acct = runtime.get_account(&vault).ok_or("vault not found")?;
240 let escrow_acct = runtime.get_account(&escrow).ok_or("escrow not found")?;
241 assert_eq!(vault_acct.owner, token_program);
243 assert_eq!(escrow_acct.owner, PROGRAM_ID);
245
246 let result = runtime.run(&take_instruction, &accounts)?;
248 assert_eq!(result.exit_code, Some(0));
250 let vault_acct = runtime.get_account(&vault).ok_or("vault not found")?;
251 let escrow_acct = runtime.get_account(&escrow).ok_or("escrow not found")?;
252 assert_eq!(vault_acct.owner, system_program);
254 assert_eq!(vault_acct.lamports, 0);
255 assert_eq!(escrow_acct.owner, system_program);
257 assert_eq!(escrow_acct.lamports, 0);
258
259 mollusk.process_and_validate_instruction_chain(
261 &[
262 (
263 &make_instruction,
264 &[
265 Check::success(),
267 Check::account(&vault).owner(&token_program).build(),
269 Check::account(&escrow).owner(&PROGRAM_ID).build(),
271 ],
272 ),
273 (
274 &take_instruction,
275 &[
276 Check::success(),
278 Check::account(&vault).owner(&system_program).build(),
280 Check::account(&vault).lamports(0).build(),
281 Check::account(&escrow).owner(&system_program).build(),
283 Check::account(&escrow).lamports(0).build(),
284 ],
285 ),
286 ],
287 &accounts,
288 );
289
290 Ok(())
291 }
292
293 #[test]
294 pub fn make_and_refund() -> Result<(), Box<dyn Error>> {
295 let mollusk = setup_mollusk();
296
297 let maker = Address::new_unique();
298 let mint_a = Address::new_unique();
299 let mint_b = Address::new_unique();
300
301 let mint_a_data = spl_token_interface::state::Mint {
302 mint_authority: None.into(),
303 supply: 1_000_000_000_000_000,
304 decimals: 6,
305 is_initialized: true,
306 freeze_authority: None.into(),
307 };
308
309 let mint_b_data = spl_token_interface::state::Mint {
310 mint_authority: None.into(),
311 supply: 1_000_000_000_000_000,
312 decimals: 6,
313 is_initialized: true,
314 freeze_authority: None.into(),
315 };
316
317 let maker_token_a_data = spl_token_interface::state::Account {
318 mint: mint_a.to_bytes().into(),
319 owner: maker.to_bytes().into(),
320 amount: 100_000_000,
321 delegate: None.into(),
322 state: spl_token_interface::state::AccountState::Initialized,
323 is_native: None.into(),
324 delegated_amount: 0,
325 close_authority: None.into(),
326 };
327
328 let (maker_token_a, maker_token_a_account) =
329 mollusk_svm_programs_token::associated_token::create_account_for_associated_token_account(maker_token_a_data);
330
331 let mut mint_a_data_bytes = vec![0u8; spl_token_interface::state::Mint::LEN];
332 let mut mint_b_data_bytes = vec![0u8; spl_token_interface::state::Mint::LEN];
333
334 mint_a_data.pack_into_slice(&mut mint_a_data_bytes);
335 mint_b_data.pack_into_slice(&mut mint_b_data_bytes);
336
337 let maker_account = Account::new(10_000_000_000, 0, &Address::default());
338 let mint_a_account = Account {
339 lamports: mollusk
340 .sysvars
341 .rent
342 .minimum_balance(spl_token_interface::state::Mint::LEN),
343 data: mint_a_data_bytes,
344 owner: mollusk_svm_programs_token::token::ID,
345 executable: false,
346 rent_epoch: 0,
347 };
348
349 let mint_b_account = Account {
350 lamports: mollusk
351 .sysvars
352 .rent
353 .minimum_balance(spl_token_interface::state::Mint::LEN),
354 data: mint_b_data_bytes,
355 owner: mollusk_svm_programs_token::token::ID,
356 executable: false,
357 rent_epoch: 0,
358 };
359
360 let (system_program, system_program_account) =
361 mollusk_svm::program::keyed_account_for_system_program();
362 let (token_program, token_program_account) =
363 mollusk_svm_programs_token::token::keyed_account();
364 let (associated_token_program, associated_token_program_account) =
365 mollusk_svm_programs_token::associated_token::keyed_account();
366
367 let seeds: &[&[u8]] = &[
368 ESCROW_SEED.as_ref(),
369 maker.as_ref(),
370 mint_a.as_ref(),
371 mint_b.as_ref(),
372 ];
373 let escrow = Address::find_program_address(seeds, &PROGRAM_ID).0;
374 let escrow_account = Account::default();
375
376 let vault = spl_associated_token_account_interface::address::get_associated_token_address(
377 &escrow, &mint_a,
378 );
379 let vault_account = Account::default();
380
381 let make_instruction = &Instruction {
382 program_id: PROGRAM_ID,
383 accounts: vec![
384 AccountMeta::new(maker, true),
385 AccountMeta::new_readonly(mint_a, false),
386 AccountMeta::new_readonly(mint_b, false),
387 AccountMeta::new(maker_token_a, false),
388 AccountMeta::new(escrow, false),
389 AccountMeta::new(vault, false),
390 AccountMeta::new_readonly(token_program, false),
391 AccountMeta::new_readonly(associated_token_program, false),
392 AccountMeta::new_readonly(system_program, false),
393 ],
394 data: vec![0, 13, 37, 0, 0, 0, 0, 0, 0, 13, 37, 0, 0, 0, 0, 0, 0],
395 };
396
397 let refund_instruction = &Instruction {
398 program_id: PROGRAM_ID,
399 accounts: vec![
400 AccountMeta::new(maker, true),
401 AccountMeta::new(maker_token_a, false),
402 AccountMeta::new(escrow, false),
403 AccountMeta::new(vault, false),
404 AccountMeta::new_readonly(token_program, false),
405 AccountMeta::new_readonly(system_program, false),
406 ],
407 data: vec![2],
408 };
409
410 let accounts = &vec![
411 (maker, maker_account.clone()),
412 (mint_a, mint_a_account.clone()),
413 (mint_b, mint_b_account.clone()),
414 (maker_token_a, maker_token_a_account.clone()),
415 (escrow, escrow_account.clone()),
416 (vault, vault_account.clone()),
417 (token_program, token_program_account.clone()),
418 (
419 associated_token_program,
420 associated_token_program_account.clone(),
421 ),
422 (system_program, system_program_account.clone()),
423 ];
424
425 let mut runtime = setup_sbpf_runtime();
427
428 let result = runtime.run(&make_instruction, &accounts)?;
430 assert_eq!(result.exit_code, Some(0));
432 let vault_acct = runtime.get_account(&vault).ok_or("vault not found")?;
433 let escrow_acct = runtime.get_account(&escrow).ok_or("escrow not found")?;
434 assert_eq!(vault_acct.owner, token_program);
436 assert_eq!(&vault_acct.data[64..72], &[13, 37, 0, 0, 0, 0, 0, 0]);
438 assert_eq!(escrow_acct.owner, PROGRAM_ID);
440 assert_eq!(&escrow_acct.data[96..104], &[13, 37, 0, 0, 0, 0, 0, 0]);
442
443 let result = runtime.run(&refund_instruction, &accounts)?;
445 assert_eq!(result.exit_code, Some(0));
447 let vault_acct = runtime.get_account(&vault).ok_or("vault not found")?;
448 let escrow_acct = runtime.get_account(&escrow).ok_or("escrow not found")?;
449 assert_eq!(vault_acct.owner, system_program);
451 assert_eq!(vault_acct.lamports, 0);
452 assert_eq!(escrow_acct.owner, system_program);
454 assert_eq!(escrow_acct.lamports, 0);
455
456 mollusk.process_and_validate_instruction_chain(
458 &[
459 (
460 &make_instruction,
461 &[
462 Check::success(),
464 Check::account(&vault).owner(&token_program).build(),
466 Check::account(&vault)
468 .data_slice(64, &[13, 37, 0, 0, 0, 0, 0, 0])
469 .build(),
470 Check::account(&escrow).owner(&PROGRAM_ID).build(),
472 Check::account(&escrow)
474 .data_slice(96, &[13, 37, 0, 0, 0, 0, 0, 0])
475 .build(),
476 ],
477 ),
478 (
479 &refund_instruction,
480 &[
481 Check::success(),
483 Check::account(&vault).owner(&system_program).build(),
485 Check::account(&vault).lamports(0).build(),
486 Check::account(&escrow).owner(&system_program).build(),
488 Check::account(&escrow).lamports(0).build(),
489 ],
490 ),
491 ],
492 &accounts,
493 );
494
495 Ok(())
496 }
497}