1pub use self::{
2 cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust},
3 logging::{
4 SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64,
5 },
6 mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
7 sysvar::{
8 SyscallGetClockSysvar, SyscallGetEpochRewardsSysvar, SyscallGetEpochScheduleSysvar,
9 SyscallGetFeesSysvar, SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
10 },
11};
12#[allow(deprecated)]
13use {
14 miraland_program_runtime::{
15 compute_budget::ComputeBudget, ic_logger_msg, ic_msg, invoke_context::InvokeContext,
16 stable_log, timings::ExecuteTimings,
17 },
18 solana_rbpf::{
19 declare_builtin_function,
20 memory_region::{AccessType, MemoryMapping},
21 program::{BuiltinFunction, BuiltinProgram, FunctionRegistry},
22 vm::Config,
23 },
24 miraland_sdk::{
25 account_info::AccountInfo,
26 alt_bn128::prelude::{
27 alt_bn128_addition, alt_bn128_multiplication, alt_bn128_pairing, AltBn128Error,
28 ALT_BN128_ADDITION_OUTPUT_LEN, ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
29 ALT_BN128_PAIRING_ELEMENT_LEN, ALT_BN128_PAIRING_OUTPUT_LEN,
30 },
31 big_mod_exp::{big_mod_exp, BigModExpParams},
32 blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
33 entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS},
34 feature_set::bpf_account_data_direct_mapping,
35 feature_set::FeatureSet,
36 feature_set::{
37 self, blake3_syscall_enabled, curve25519_syscall_enabled,
38 disable_deploy_of_alloc_free_syscall, disable_fees_sysvar,
39 enable_alt_bn128_compression_syscall, enable_alt_bn128_syscall,
40 enable_big_mod_exp_syscall, enable_partitioned_epoch_reward, enable_poseidon_syscall,
41 error_on_syscall_bpf_function_hash_collisions, last_restart_slot_sysvar,
42 reject_callx_r10, remaining_compute_units_syscall_enabled, switch_to_new_elf_parser,
43 },
44 hash::{Hash, Hasher},
45 instruction::{AccountMeta, InstructionError, ProcessedSiblingInstruction},
46 keccak, native_loader, poseidon,
47 precompiles::is_precompile,
48 program::MAX_RETURN_DATA,
49 program_stubs::is_nonoverlapping,
50 pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN},
51 secp256k1_recover::{
52 Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
53 },
54 sysvar::{Sysvar, SysvarId},
55 transaction_context::{IndexOfAccount, InstructionAccount},
56 },
57 std::{
58 alloc::Layout,
59 mem::{align_of, size_of},
60 slice::from_raw_parts_mut,
61 str::{from_utf8, Utf8Error},
62 sync::Arc,
63 },
64 thiserror::Error as ThisError,
65};
66
67mod cpi;
68mod logging;
69mod mem_ops;
70mod sysvar;
71
72pub const MAX_SIGNERS: usize = 16;
74
75#[derive(Debug, ThisError, PartialEq, Eq)]
77pub enum SyscallError {
78 #[error("{0}: {1:?}")]
79 InvalidString(Utf8Error, Vec<u8>),
80 #[error("SBF program panicked")]
81 Abort,
82 #[error("SBF program Panicked in {0} at {1}:{2}")]
83 Panic(String, u64, u64),
84 #[error("Cannot borrow invoke context")]
85 InvokeContextBorrowFailed,
86 #[error("Malformed signer seed: {0}: {1:?}")]
87 MalformedSignerSeed(Utf8Error, Vec<u8>),
88 #[error("Could not create program address with signer seeds: {0}")]
89 BadSeeds(PubkeyError),
90 #[error("Program {0} not supported by inner instructions")]
91 ProgramNotSupported(Pubkey),
92 #[error("Unaligned pointer")]
93 UnalignedPointer,
94 #[error("Too many signers")]
95 TooManySigners,
96 #[error("Instruction passed to inner instruction is too large ({0} > {1})")]
97 InstructionTooLarge(usize, usize),
98 #[error("Too many accounts passed to inner instruction")]
99 TooManyAccounts,
100 #[error("Overlapping copy")]
101 CopyOverlapping,
102 #[error("Return data too large ({0} > {1})")]
103 ReturnDataTooLarge(u64, u64),
104 #[error("Hashing too many sequences")]
105 TooManySlices,
106 #[error("InvalidLength")]
107 InvalidLength,
108 #[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")]
109 MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 },
110 #[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")]
111 MaxInstructionAccountsExceeded {
112 num_accounts: u64,
113 max_accounts: u64,
114 },
115 #[error("Invoked an instruction with too many account info's ({num_account_infos} > {max_account_infos})")]
116 MaxInstructionAccountInfosExceeded {
117 num_account_infos: u64,
118 max_account_infos: u64,
119 },
120 #[error("InvalidAttribute")]
121 InvalidAttribute,
122 #[error("Invalid pointer")]
123 InvalidPointer,
124 #[error("Arithmetic overflow")]
125 ArithmeticOverflow,
126}
127
128type Error = Box<dyn std::error::Error>;
129
130pub trait HasherImpl {
131 const NAME: &'static str;
132 type Output: AsRef<[u8]>;
133
134 fn create_hasher() -> Self;
135 fn hash(&mut self, val: &[u8]);
136 fn result(self) -> Self::Output;
137 fn get_base_cost(compute_budget: &ComputeBudget) -> u64;
138 fn get_byte_cost(compute_budget: &ComputeBudget) -> u64;
139 fn get_max_slices(compute_budget: &ComputeBudget) -> u64;
140}
141
142pub struct Sha256Hasher(Hasher);
143pub struct Blake3Hasher(blake3::Hasher);
144pub struct Keccak256Hasher(keccak::Hasher);
145
146impl HasherImpl for Sha256Hasher {
147 const NAME: &'static str = "Sha256";
148 type Output = Hash;
149
150 fn create_hasher() -> Self {
151 Sha256Hasher(Hasher::default())
152 }
153
154 fn hash(&mut self, val: &[u8]) {
155 self.0.hash(val);
156 }
157
158 fn result(self) -> Self::Output {
159 self.0.result()
160 }
161
162 fn get_base_cost(compute_budget: &ComputeBudget) -> u64 {
163 compute_budget.sha256_base_cost
164 }
165 fn get_byte_cost(compute_budget: &ComputeBudget) -> u64 {
166 compute_budget.sha256_byte_cost
167 }
168 fn get_max_slices(compute_budget: &ComputeBudget) -> u64 {
169 compute_budget.sha256_max_slices
170 }
171}
172
173impl HasherImpl for Blake3Hasher {
174 const NAME: &'static str = "Blake3";
175 type Output = blake3::Hash;
176
177 fn create_hasher() -> Self {
178 Blake3Hasher(blake3::Hasher::default())
179 }
180
181 fn hash(&mut self, val: &[u8]) {
182 self.0.hash(val);
183 }
184
185 fn result(self) -> Self::Output {
186 self.0.result()
187 }
188
189 fn get_base_cost(compute_budget: &ComputeBudget) -> u64 {
190 compute_budget.sha256_base_cost
191 }
192 fn get_byte_cost(compute_budget: &ComputeBudget) -> u64 {
193 compute_budget.sha256_byte_cost
194 }
195 fn get_max_slices(compute_budget: &ComputeBudget) -> u64 {
196 compute_budget.sha256_max_slices
197 }
198}
199
200impl HasherImpl for Keccak256Hasher {
201 const NAME: &'static str = "Keccak256";
202 type Output = keccak::Hash;
203
204 fn create_hasher() -> Self {
205 Keccak256Hasher(keccak::Hasher::default())
206 }
207
208 fn hash(&mut self, val: &[u8]) {
209 self.0.hash(val);
210 }
211
212 fn result(self) -> Self::Output {
213 self.0.result()
214 }
215
216 fn get_base_cost(compute_budget: &ComputeBudget) -> u64 {
217 compute_budget.sha256_base_cost
218 }
219 fn get_byte_cost(compute_budget: &ComputeBudget) -> u64 {
220 compute_budget.sha256_byte_cost
221 }
222 fn get_max_slices(compute_budget: &ComputeBudget) -> u64 {
223 compute_budget.sha256_max_slices
224 }
225}
226
227fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), Error> {
228 invoke_context.consume_checked(amount)?;
229 Ok(())
230}
231
232macro_rules! register_feature_gated_function {
233 ($result:expr, $is_feature_active:expr, $name:expr, $call:expr $(,)?) => {
234 if $is_feature_active {
235 $result.register_function_hashed($name, $call)
236 } else {
237 Ok(0)
238 }
239 };
240}
241
242pub fn create_program_runtime_environment_v1<'a>(
243 feature_set: &FeatureSet,
244 compute_budget: &ComputeBudget,
245 reject_deployment_of_broken_elfs: bool,
246 debugging_features: bool,
247) -> Result<BuiltinProgram<InvokeContext<'a>>, Error> {
248 let enable_alt_bn128_syscall = feature_set.is_active(&enable_alt_bn128_syscall::id());
249 let enable_alt_bn128_compression_syscall =
250 feature_set.is_active(&enable_alt_bn128_compression_syscall::id());
251 let enable_big_mod_exp_syscall = feature_set.is_active(&enable_big_mod_exp_syscall::id());
252 let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id());
253 let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id());
254 let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id());
255 let epoch_rewards_syscall_enabled =
256 feature_set.is_active(&enable_partitioned_epoch_reward::id());
257 let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
258 && feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id());
259 let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id());
260 let enable_poseidon_syscall = feature_set.is_active(&enable_poseidon_syscall::id());
261 let remaining_compute_units_syscall_enabled =
262 feature_set.is_active(&remaining_compute_units_syscall_enabled::id());
263 let config = Config {
268 max_call_depth: compute_budget.max_call_depth,
269 stack_frame_size: compute_budget.stack_frame_size,
270 enable_address_translation: true,
271 enable_stack_frame_gaps: !feature_set.is_active(&bpf_account_data_direct_mapping::id()),
272 instruction_meter_checkpoint_distance: 10000,
273 enable_instruction_meter: true,
274 enable_instruction_tracing: debugging_features,
275 enable_symbol_and_section_labels: debugging_features,
276 reject_broken_elfs: reject_deployment_of_broken_elfs,
277 noop_instruction_rate: 256,
278 sanitize_user_provided_values: true,
279 external_internal_function_hash_collision: feature_set
280 .is_active(&error_on_syscall_bpf_function_hash_collisions::id()),
281 reject_callx_r10: feature_set.is_active(&reject_callx_r10::id()),
282 enable_sbpf_v1: true,
283 enable_sbpf_v2: false,
284 optimize_rodata: false,
285 new_elf_parser: feature_set.is_active(&switch_to_new_elf_parser::id()),
286 aligned_memory_mapping: !feature_set.is_active(&bpf_account_data_direct_mapping::id()),
287 };
289 let mut result = FunctionRegistry::<BuiltinFunction<InvokeContext>>::default();
290
291 result.register_function_hashed(*b"abort", SyscallAbort::vm)?;
293
294 result.register_function_hashed(*b"sol_panic_", SyscallPanic::vm)?;
296
297 result.register_function_hashed(*b"sol_log_", SyscallLog::vm)?;
299 result.register_function_hashed(*b"sol_log_64_", SyscallLogU64::vm)?;
300 result.register_function_hashed(*b"sol_log_compute_units_", SyscallLogBpfComputeUnits::vm)?;
301 result.register_function_hashed(*b"sol_log_pubkey", SyscallLogPubkey::vm)?;
302
303 result.register_function_hashed(
305 *b"sol_create_program_address",
306 SyscallCreateProgramAddress::vm,
307 )?;
308 result.register_function_hashed(
309 *b"sol_try_find_program_address",
310 SyscallTryFindProgramAddress::vm,
311 )?;
312
313 result.register_function_hashed(*b"sol_sha256", SyscallHash::vm::<Sha256Hasher>)?;
315
316 result.register_function_hashed(*b"sol_keccak256", SyscallHash::vm::<Keccak256Hasher>)?;
318
319 result.register_function_hashed(*b"sol_secp256k1_recover", SyscallSecp256k1Recover::vm)?;
321
322 register_feature_gated_function!(
324 result,
325 blake3_syscall_enabled,
326 *b"sol_blake3",
327 SyscallHash::vm::<Blake3Hasher>,
328 )?;
329
330 register_feature_gated_function!(
332 result,
333 curve25519_syscall_enabled,
334 *b"sol_curve_validate_point",
335 SyscallCurvePointValidation::vm,
336 )?;
337 register_feature_gated_function!(
338 result,
339 curve25519_syscall_enabled,
340 *b"sol_curve_group_op",
341 SyscallCurveGroupOps::vm,
342 )?;
343 register_feature_gated_function!(
344 result,
345 curve25519_syscall_enabled,
346 *b"sol_curve_multiscalar_mul",
347 SyscallCurveMultiscalarMultiplication::vm,
348 )?;
349
350 result.register_function_hashed(*b"sol_get_clock_sysvar", SyscallGetClockSysvar::vm)?;
352 result.register_function_hashed(
353 *b"sol_get_epoch_schedule_sysvar",
354 SyscallGetEpochScheduleSysvar::vm,
355 )?;
356 register_feature_gated_function!(
357 result,
358 !disable_fees_sysvar,
359 *b"sol_get_fees_sysvar",
360 SyscallGetFeesSysvar::vm,
361 )?;
362 result.register_function_hashed(*b"sol_get_rent_sysvar", SyscallGetRentSysvar::vm)?;
363
364 register_feature_gated_function!(
365 result,
366 last_restart_slot_syscall_enabled,
367 *b"sol_get_last_restart_slot",
368 SyscallGetLastRestartSlotSysvar::vm,
369 )?;
370
371 register_feature_gated_function!(
372 result,
373 epoch_rewards_syscall_enabled,
374 *b"sol_get_epoch_rewards_sysvar",
375 SyscallGetEpochRewardsSysvar::vm,
376 )?;
377
378 result.register_function_hashed(*b"sol_memcpy_", SyscallMemcpy::vm)?;
380 result.register_function_hashed(*b"sol_memmove_", SyscallMemmove::vm)?;
381 result.register_function_hashed(*b"sol_memcmp_", SyscallMemcmp::vm)?;
382 result.register_function_hashed(*b"sol_memset_", SyscallMemset::vm)?;
383
384 result.register_function_hashed(
386 *b"sol_get_processed_sibling_instruction",
387 SyscallGetProcessedSiblingInstruction::vm,
388 )?;
389
390 result.register_function_hashed(*b"sol_get_stack_height", SyscallGetStackHeight::vm)?;
392
393 result.register_function_hashed(*b"sol_set_return_data", SyscallSetReturnData::vm)?;
395 result.register_function_hashed(*b"sol_get_return_data", SyscallGetReturnData::vm)?;
396
397 result.register_function_hashed(*b"sol_invoke_signed_c", SyscallInvokeSignedC::vm)?;
399 result.register_function_hashed(*b"sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)?;
400
401 register_feature_gated_function!(
403 result,
404 !disable_deploy_of_alloc_free_syscall,
405 *b"sol_alloc_free_",
406 SyscallAllocFree::vm,
407 )?;
408
409 register_feature_gated_function!(
411 result,
412 enable_alt_bn128_syscall,
413 *b"sol_alt_bn128_group_op",
414 SyscallAltBn128::vm,
415 )?;
416
417 register_feature_gated_function!(
419 result,
420 enable_big_mod_exp_syscall,
421 *b"sol_big_mod_exp",
422 SyscallBigModExp::vm,
423 )?;
424
425 register_feature_gated_function!(
427 result,
428 enable_poseidon_syscall,
429 *b"sol_poseidon",
430 SyscallPoseidon::vm,
431 )?;
432
433 register_feature_gated_function!(
435 result,
436 remaining_compute_units_syscall_enabled,
437 *b"sol_remaining_compute_units",
438 SyscallRemainingComputeUnits::vm
439 )?;
440
441 register_feature_gated_function!(
443 result,
444 enable_alt_bn128_compression_syscall,
445 *b"sol_alt_bn128_compression",
446 SyscallAltBn128Compression::vm,
447 )?;
448
449 result.register_function_hashed(*b"sol_log_data", SyscallLogData::vm)?;
451
452 Ok(BuiltinProgram::new_loader(config, result))
453}
454
455fn address_is_aligned<T>(address: u64) -> bool {
456 (address as *mut T as usize)
457 .checked_rem(align_of::<T>())
458 .map(|rem| rem == 0)
459 .expect("T to be non-zero aligned")
460}
461
462fn translate(
463 memory_mapping: &MemoryMapping,
464 access_type: AccessType,
465 vm_addr: u64,
466 len: u64,
467) -> Result<u64, Error> {
468 memory_mapping
469 .map(access_type, vm_addr, len)
470 .map_err(|err| err.into())
471 .into()
472}
473
474fn translate_type_inner<'a, T>(
475 memory_mapping: &MemoryMapping,
476 access_type: AccessType,
477 vm_addr: u64,
478 check_aligned: bool,
479) -> Result<&'a mut T, Error> {
480 let host_addr = translate(memory_mapping, access_type, vm_addr, size_of::<T>() as u64)?;
481 if !check_aligned {
482 Ok(unsafe { std::mem::transmute::<u64, &mut T>(host_addr) })
483 } else if !address_is_aligned::<T>(host_addr) {
484 Err(SyscallError::UnalignedPointer.into())
485 } else {
486 Ok(unsafe { &mut *(host_addr as *mut T) })
487 }
488}
489fn translate_type_mut<'a, T>(
490 memory_mapping: &MemoryMapping,
491 vm_addr: u64,
492 check_aligned: bool,
493) -> Result<&'a mut T, Error> {
494 translate_type_inner::<T>(memory_mapping, AccessType::Store, vm_addr, check_aligned)
495}
496fn translate_type<'a, T>(
497 memory_mapping: &MemoryMapping,
498 vm_addr: u64,
499 check_aligned: bool,
500) -> Result<&'a T, Error> {
501 translate_type_inner::<T>(memory_mapping, AccessType::Load, vm_addr, check_aligned)
502 .map(|value| &*value)
503}
504
505fn translate_slice_inner<'a, T>(
506 memory_mapping: &MemoryMapping,
507 access_type: AccessType,
508 vm_addr: u64,
509 len: u64,
510 check_aligned: bool,
511) -> Result<&'a mut [T], Error> {
512 if len == 0 {
513 return Ok(&mut []);
514 }
515
516 let total_size = len.saturating_mul(size_of::<T>() as u64);
517 if isize::try_from(total_size).is_err() {
518 return Err(SyscallError::InvalidLength.into());
519 }
520
521 let host_addr = translate(memory_mapping, access_type, vm_addr, total_size)?;
522
523 if check_aligned && !address_is_aligned::<T>(host_addr) {
524 return Err(SyscallError::UnalignedPointer.into());
525 }
526 Ok(unsafe { from_raw_parts_mut(host_addr as *mut T, len as usize) })
527}
528fn translate_slice_mut<'a, T>(
529 memory_mapping: &MemoryMapping,
530 vm_addr: u64,
531 len: u64,
532 check_aligned: bool,
533) -> Result<&'a mut [T], Error> {
534 translate_slice_inner::<T>(
535 memory_mapping,
536 AccessType::Store,
537 vm_addr,
538 len,
539 check_aligned,
540 )
541}
542fn translate_slice<'a, T>(
543 memory_mapping: &MemoryMapping,
544 vm_addr: u64,
545 len: u64,
546 check_aligned: bool,
547) -> Result<&'a [T], Error> {
548 translate_slice_inner::<T>(
549 memory_mapping,
550 AccessType::Load,
551 vm_addr,
552 len,
553 check_aligned,
554 )
555 .map(|value| &*value)
556}
557
558fn translate_string_and_do(
561 memory_mapping: &MemoryMapping,
562 addr: u64,
563 len: u64,
564 check_aligned: bool,
565 work: &mut dyn FnMut(&str) -> Result<u64, Error>,
566) -> Result<u64, Error> {
567 let buf = translate_slice::<u8>(memory_mapping, addr, len, check_aligned)?;
568 match from_utf8(buf) {
569 Ok(message) => work(message),
570 Err(err) => Err(SyscallError::InvalidString(err, buf.to_vec()).into()),
571 }
572}
573
574declare_builtin_function!(
575 SyscallAbort,
580 fn rust(
581 _invoke_context: &mut InvokeContext,
582 _arg1: u64,
583 _arg2: u64,
584 _arg3: u64,
585 _arg4: u64,
586 _arg5: u64,
587 _memory_mapping: &mut MemoryMapping,
588 ) -> Result<u64, Error> {
589 Err(SyscallError::Abort.into())
590 }
591);
592
593declare_builtin_function!(
594 SyscallPanic,
597 fn rust(
598 invoke_context: &mut InvokeContext,
599 file: u64,
600 len: u64,
601 line: u64,
602 column: u64,
603 _arg5: u64,
604 memory_mapping: &mut MemoryMapping,
605 ) -> Result<u64, Error> {
606 consume_compute_meter(invoke_context, len)?;
607
608 translate_string_and_do(
609 memory_mapping,
610 file,
611 len,
612 invoke_context.get_check_aligned(),
613 &mut |string: &str| Err(SyscallError::Panic(string.to_string(), line, column).into()),
614 )
615 }
616);
617
618declare_builtin_function!(
619 SyscallAllocFree,
626 fn rust(
627 invoke_context: &mut InvokeContext,
628 size: u64,
629 free_addr: u64,
630 _arg3: u64,
631 _arg4: u64,
632 _arg5: u64,
633 _memory_mapping: &mut MemoryMapping,
634 ) -> Result<u64, Error> {
635 let align = if invoke_context.get_check_aligned() {
636 BPF_ALIGN_OF_U128
637 } else {
638 align_of::<u8>()
639 };
640 let Ok(layout) = Layout::from_size_align(size as usize, align) else {
641 return Ok(0);
642 };
643 let allocator = &mut invoke_context.get_syscall_context_mut()?.allocator;
644 if free_addr == 0 {
645 match allocator.alloc(layout) {
646 Ok(addr) => Ok(addr),
647 Err(_) => Ok(0),
648 }
649 } else {
650 Ok(0)
652 }
653 }
654);
655
656fn translate_and_check_program_address_inputs<'a>(
657 seeds_addr: u64,
658 seeds_len: u64,
659 program_id_addr: u64,
660 memory_mapping: &mut MemoryMapping,
661 check_aligned: bool,
662) -> Result<(Vec<&'a [u8]>, &'a Pubkey), Error> {
663 let untranslated_seeds =
664 translate_slice::<&[u8]>(memory_mapping, seeds_addr, seeds_len, check_aligned)?;
665 if untranslated_seeds.len() > MAX_SEEDS {
666 return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
667 }
668 let seeds = untranslated_seeds
669 .iter()
670 .map(|untranslated_seed| {
671 if untranslated_seed.len() > MAX_SEED_LEN {
672 return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
673 }
674 translate_slice::<u8>(
675 memory_mapping,
676 untranslated_seed.as_ptr() as *const _ as u64,
677 untranslated_seed.len() as u64,
678 check_aligned,
679 )
680 })
681 .collect::<Result<Vec<_>, Error>>()?;
682 let program_id = translate_type::<Pubkey>(memory_mapping, program_id_addr, check_aligned)?;
683 Ok((seeds, program_id))
684}
685
686declare_builtin_function!(
687 SyscallCreateProgramAddress,
689 fn rust(
690 invoke_context: &mut InvokeContext,
691 seeds_addr: u64,
692 seeds_len: u64,
693 program_id_addr: u64,
694 address_addr: u64,
695 _arg5: u64,
696 memory_mapping: &mut MemoryMapping,
697 ) -> Result<u64, Error> {
698 let cost = invoke_context
699 .get_compute_budget()
700 .create_program_address_units;
701 consume_compute_meter(invoke_context, cost)?;
702
703 let (seeds, program_id) = translate_and_check_program_address_inputs(
704 seeds_addr,
705 seeds_len,
706 program_id_addr,
707 memory_mapping,
708 invoke_context.get_check_aligned(),
709 )?;
710
711 let Ok(new_address) = Pubkey::create_program_address(&seeds, program_id) else {
712 return Ok(1);
713 };
714 let address = translate_slice_mut::<u8>(
715 memory_mapping,
716 address_addr,
717 32,
718 invoke_context.get_check_aligned(),
719 )?;
720 address.copy_from_slice(new_address.as_ref());
721 Ok(0)
722 }
723);
724
725declare_builtin_function!(
726 SyscallTryFindProgramAddress,
728 fn rust(
729 invoke_context: &mut InvokeContext,
730 seeds_addr: u64,
731 seeds_len: u64,
732 program_id_addr: u64,
733 address_addr: u64,
734 bump_seed_addr: u64,
735 memory_mapping: &mut MemoryMapping,
736 ) -> Result<u64, Error> {
737 let cost = invoke_context
738 .get_compute_budget()
739 .create_program_address_units;
740 consume_compute_meter(invoke_context, cost)?;
741
742 let (seeds, program_id) = translate_and_check_program_address_inputs(
743 seeds_addr,
744 seeds_len,
745 program_id_addr,
746 memory_mapping,
747 invoke_context.get_check_aligned(),
748 )?;
749
750 let mut bump_seed = [std::u8::MAX];
751 for _ in 0..std::u8::MAX {
752 {
753 let mut seeds_with_bump = seeds.to_vec();
754 seeds_with_bump.push(&bump_seed);
755
756 if let Ok(new_address) =
757 Pubkey::create_program_address(&seeds_with_bump, program_id)
758 {
759 let bump_seed_ref = translate_type_mut::<u8>(
760 memory_mapping,
761 bump_seed_addr,
762 invoke_context.get_check_aligned(),
763 )?;
764 let address = translate_slice_mut::<u8>(
765 memory_mapping,
766 address_addr,
767 std::mem::size_of::<Pubkey>() as u64,
768 invoke_context.get_check_aligned(),
769 )?;
770 if !is_nonoverlapping(
771 bump_seed_ref as *const _ as usize,
772 std::mem::size_of_val(bump_seed_ref),
773 address.as_ptr() as usize,
774 std::mem::size_of::<Pubkey>(),
775 ) {
776 return Err(SyscallError::CopyOverlapping.into());
777 }
778 *bump_seed_ref = bump_seed[0];
779 address.copy_from_slice(new_address.as_ref());
780 return Ok(0);
781 }
782 }
783 bump_seed[0] = bump_seed[0].saturating_sub(1);
784 consume_compute_meter(invoke_context, cost)?;
785 }
786 Ok(1)
787 }
788);
789
790declare_builtin_function!(
791 SyscallSecp256k1Recover,
793 fn rust(
794 invoke_context: &mut InvokeContext,
795 hash_addr: u64,
796 recovery_id_val: u64,
797 signature_addr: u64,
798 result_addr: u64,
799 _arg5: u64,
800 memory_mapping: &mut MemoryMapping,
801 ) -> Result<u64, Error> {
802 let cost = invoke_context.get_compute_budget().secp256k1_recover_cost;
803 consume_compute_meter(invoke_context, cost)?;
804
805 let hash = translate_slice::<u8>(
806 memory_mapping,
807 hash_addr,
808 keccak::HASH_BYTES as u64,
809 invoke_context.get_check_aligned(),
810 )?;
811 let signature = translate_slice::<u8>(
812 memory_mapping,
813 signature_addr,
814 SECP256K1_SIGNATURE_LENGTH as u64,
815 invoke_context.get_check_aligned(),
816 )?;
817 let secp256k1_recover_result = translate_slice_mut::<u8>(
818 memory_mapping,
819 result_addr,
820 SECP256K1_PUBLIC_KEY_LENGTH as u64,
821 invoke_context.get_check_aligned(),
822 )?;
823
824 let Ok(message) = libsecp256k1::Message::parse_slice(hash) else {
825 return Ok(Secp256k1RecoverError::InvalidHash.into());
826 };
827 let Ok(adjusted_recover_id_val) = recovery_id_val.try_into() else {
828 return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
829 };
830 let Ok(recovery_id) = libsecp256k1::RecoveryId::parse(adjusted_recover_id_val) else {
831 return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
832 };
833 let Ok(signature) = libsecp256k1::Signature::parse_standard_slice(signature) else {
834 return Ok(Secp256k1RecoverError::InvalidSignature.into());
835 };
836
837 let public_key = match libsecp256k1::recover(&message, &signature, &recovery_id) {
838 Ok(key) => key.serialize(),
839 Err(_) => {
840 return Ok(Secp256k1RecoverError::InvalidSignature.into());
841 }
842 };
843
844 secp256k1_recover_result.copy_from_slice(&public_key[1..65]);
845 Ok(SUCCESS)
846 }
847);
848
849declare_builtin_function!(
850 SyscallCurvePointValidation,
854 fn rust(
855 invoke_context: &mut InvokeContext,
856 curve_id: u64,
857 point_addr: u64,
858 _arg3: u64,
859 _arg4: u64,
860 _arg5: u64,
861 memory_mapping: &mut MemoryMapping,
862 ) -> Result<u64, Error> {
863 use miraland_zk_token_sdk::curve25519::{curve_syscall_traits::*, edwards, ristretto};
864 match curve_id {
865 CURVE25519_EDWARDS => {
866 let cost = invoke_context
867 .get_compute_budget()
868 .curve25519_edwards_validate_point_cost;
869 consume_compute_meter(invoke_context, cost)?;
870
871 let point = translate_type::<edwards::PodEdwardsPoint>(
872 memory_mapping,
873 point_addr,
874 invoke_context.get_check_aligned(),
875 )?;
876
877 if edwards::validate_edwards(point) {
878 Ok(0)
879 } else {
880 Ok(1)
881 }
882 }
883 CURVE25519_RISTRETTO => {
884 let cost = invoke_context
885 .get_compute_budget()
886 .curve25519_ristretto_validate_point_cost;
887 consume_compute_meter(invoke_context, cost)?;
888
889 let point = translate_type::<ristretto::PodRistrettoPoint>(
890 memory_mapping,
891 point_addr,
892 invoke_context.get_check_aligned(),
893 )?;
894
895 if ristretto::validate_ristretto(point) {
896 Ok(0)
897 } else {
898 Ok(1)
899 }
900 }
901 _ => Ok(1),
902 }
903 }
904);
905
906declare_builtin_function!(
907 SyscallCurveGroupOps,
911 fn rust(
912 invoke_context: &mut InvokeContext,
913 curve_id: u64,
914 group_op: u64,
915 left_input_addr: u64,
916 right_input_addr: u64,
917 result_point_addr: u64,
918 memory_mapping: &mut MemoryMapping,
919 ) -> Result<u64, Error> {
920 use miraland_zk_token_sdk::curve25519::{
921 curve_syscall_traits::*, edwards, ristretto, scalar,
922 };
923 match curve_id {
924 CURVE25519_EDWARDS => match group_op {
925 ADD => {
926 let cost = invoke_context
927 .get_compute_budget()
928 .curve25519_edwards_add_cost;
929 consume_compute_meter(invoke_context, cost)?;
930
931 let left_point = translate_type::<edwards::PodEdwardsPoint>(
932 memory_mapping,
933 left_input_addr,
934 invoke_context.get_check_aligned(),
935 )?;
936 let right_point = translate_type::<edwards::PodEdwardsPoint>(
937 memory_mapping,
938 right_input_addr,
939 invoke_context.get_check_aligned(),
940 )?;
941
942 if let Some(result_point) = edwards::add_edwards(left_point, right_point) {
943 *translate_type_mut::<edwards::PodEdwardsPoint>(
944 memory_mapping,
945 result_point_addr,
946 invoke_context.get_check_aligned(),
947 )? = result_point;
948 Ok(0)
949 } else {
950 Ok(1)
951 }
952 }
953 SUB => {
954 let cost = invoke_context
955 .get_compute_budget()
956 .curve25519_edwards_subtract_cost;
957 consume_compute_meter(invoke_context, cost)?;
958
959 let left_point = translate_type::<edwards::PodEdwardsPoint>(
960 memory_mapping,
961 left_input_addr,
962 invoke_context.get_check_aligned(),
963 )?;
964 let right_point = translate_type::<edwards::PodEdwardsPoint>(
965 memory_mapping,
966 right_input_addr,
967 invoke_context.get_check_aligned(),
968 )?;
969
970 if let Some(result_point) = edwards::subtract_edwards(left_point, right_point) {
971 *translate_type_mut::<edwards::PodEdwardsPoint>(
972 memory_mapping,
973 result_point_addr,
974 invoke_context.get_check_aligned(),
975 )? = result_point;
976 Ok(0)
977 } else {
978 Ok(1)
979 }
980 }
981 MUL => {
982 let cost = invoke_context
983 .get_compute_budget()
984 .curve25519_edwards_multiply_cost;
985 consume_compute_meter(invoke_context, cost)?;
986
987 let scalar = translate_type::<scalar::PodScalar>(
988 memory_mapping,
989 left_input_addr,
990 invoke_context.get_check_aligned(),
991 )?;
992 let input_point = translate_type::<edwards::PodEdwardsPoint>(
993 memory_mapping,
994 right_input_addr,
995 invoke_context.get_check_aligned(),
996 )?;
997
998 if let Some(result_point) = edwards::multiply_edwards(scalar, input_point) {
999 *translate_type_mut::<edwards::PodEdwardsPoint>(
1000 memory_mapping,
1001 result_point_addr,
1002 invoke_context.get_check_aligned(),
1003 )? = result_point;
1004 Ok(0)
1005 } else {
1006 Ok(1)
1007 }
1008 }
1009 _ => Ok(1),
1010 },
1011
1012 CURVE25519_RISTRETTO => match group_op {
1013 ADD => {
1014 let cost = invoke_context
1015 .get_compute_budget()
1016 .curve25519_ristretto_add_cost;
1017 consume_compute_meter(invoke_context, cost)?;
1018
1019 let left_point = translate_type::<ristretto::PodRistrettoPoint>(
1020 memory_mapping,
1021 left_input_addr,
1022 invoke_context.get_check_aligned(),
1023 )?;
1024 let right_point = translate_type::<ristretto::PodRistrettoPoint>(
1025 memory_mapping,
1026 right_input_addr,
1027 invoke_context.get_check_aligned(),
1028 )?;
1029
1030 if let Some(result_point) = ristretto::add_ristretto(left_point, right_point) {
1031 *translate_type_mut::<ristretto::PodRistrettoPoint>(
1032 memory_mapping,
1033 result_point_addr,
1034 invoke_context.get_check_aligned(),
1035 )? = result_point;
1036 Ok(0)
1037 } else {
1038 Ok(1)
1039 }
1040 }
1041 SUB => {
1042 let cost = invoke_context
1043 .get_compute_budget()
1044 .curve25519_ristretto_subtract_cost;
1045 consume_compute_meter(invoke_context, cost)?;
1046
1047 let left_point = translate_type::<ristretto::PodRistrettoPoint>(
1048 memory_mapping,
1049 left_input_addr,
1050 invoke_context.get_check_aligned(),
1051 )?;
1052 let right_point = translate_type::<ristretto::PodRistrettoPoint>(
1053 memory_mapping,
1054 right_input_addr,
1055 invoke_context.get_check_aligned(),
1056 )?;
1057
1058 if let Some(result_point) =
1059 ristretto::subtract_ristretto(left_point, right_point)
1060 {
1061 *translate_type_mut::<ristretto::PodRistrettoPoint>(
1062 memory_mapping,
1063 result_point_addr,
1064 invoke_context.get_check_aligned(),
1065 )? = result_point;
1066 Ok(0)
1067 } else {
1068 Ok(1)
1069 }
1070 }
1071 MUL => {
1072 let cost = invoke_context
1073 .get_compute_budget()
1074 .curve25519_ristretto_multiply_cost;
1075 consume_compute_meter(invoke_context, cost)?;
1076
1077 let scalar = translate_type::<scalar::PodScalar>(
1078 memory_mapping,
1079 left_input_addr,
1080 invoke_context.get_check_aligned(),
1081 )?;
1082 let input_point = translate_type::<ristretto::PodRistrettoPoint>(
1083 memory_mapping,
1084 right_input_addr,
1085 invoke_context.get_check_aligned(),
1086 )?;
1087
1088 if let Some(result_point) = ristretto::multiply_ristretto(scalar, input_point) {
1089 *translate_type_mut::<ristretto::PodRistrettoPoint>(
1090 memory_mapping,
1091 result_point_addr,
1092 invoke_context.get_check_aligned(),
1093 )? = result_point;
1094 Ok(0)
1095 } else {
1096 Ok(1)
1097 }
1098 }
1099 _ => Ok(1),
1100 },
1101
1102 _ => Ok(1),
1103 }
1104 }
1105);
1106
1107declare_builtin_function!(
1108 SyscallCurveMultiscalarMultiplication,
1112 fn rust(
1113 invoke_context: &mut InvokeContext,
1114 curve_id: u64,
1115 scalars_addr: u64,
1116 points_addr: u64,
1117 points_len: u64,
1118 result_point_addr: u64,
1119 memory_mapping: &mut MemoryMapping,
1120 ) -> Result<u64, Error> {
1121 use miraland_zk_token_sdk::curve25519::{
1122 curve_syscall_traits::*, edwards, ristretto, scalar,
1123 };
1124
1125 let restrict_msm_length = invoke_context
1126 .feature_set
1127 .is_active(&feature_set::curve25519_restrict_msm_length::id());
1128 #[allow(clippy::collapsible_if)]
1129 if restrict_msm_length {
1130 if points_len > 512 {
1131 return Err(Box::new(SyscallError::InvalidLength));
1132 }
1133 }
1134
1135 match curve_id {
1136 CURVE25519_EDWARDS => {
1137 let cost = invoke_context
1138 .get_compute_budget()
1139 .curve25519_edwards_msm_base_cost
1140 .saturating_add(
1141 invoke_context
1142 .get_compute_budget()
1143 .curve25519_edwards_msm_incremental_cost
1144 .saturating_mul(points_len.saturating_sub(1)),
1145 );
1146 consume_compute_meter(invoke_context, cost)?;
1147
1148 let scalars = translate_slice::<scalar::PodScalar>(
1149 memory_mapping,
1150 scalars_addr,
1151 points_len,
1152 invoke_context.get_check_aligned(),
1153 )?;
1154
1155 let points = translate_slice::<edwards::PodEdwardsPoint>(
1156 memory_mapping,
1157 points_addr,
1158 points_len,
1159 invoke_context.get_check_aligned(),
1160 )?;
1161
1162 if let Some(result_point) = edwards::multiscalar_multiply_edwards(scalars, points) {
1163 *translate_type_mut::<edwards::PodEdwardsPoint>(
1164 memory_mapping,
1165 result_point_addr,
1166 invoke_context.get_check_aligned(),
1167 )? = result_point;
1168 Ok(0)
1169 } else {
1170 Ok(1)
1171 }
1172 }
1173
1174 CURVE25519_RISTRETTO => {
1175 let cost = invoke_context
1176 .get_compute_budget()
1177 .curve25519_ristretto_msm_base_cost
1178 .saturating_add(
1179 invoke_context
1180 .get_compute_budget()
1181 .curve25519_ristretto_msm_incremental_cost
1182 .saturating_mul(points_len.saturating_sub(1)),
1183 );
1184 consume_compute_meter(invoke_context, cost)?;
1185
1186 let scalars = translate_slice::<scalar::PodScalar>(
1187 memory_mapping,
1188 scalars_addr,
1189 points_len,
1190 invoke_context.get_check_aligned(),
1191 )?;
1192
1193 let points = translate_slice::<ristretto::PodRistrettoPoint>(
1194 memory_mapping,
1195 points_addr,
1196 points_len,
1197 invoke_context.get_check_aligned(),
1198 )?;
1199
1200 if let Some(result_point) =
1201 ristretto::multiscalar_multiply_ristretto(scalars, points)
1202 {
1203 *translate_type_mut::<ristretto::PodRistrettoPoint>(
1204 memory_mapping,
1205 result_point_addr,
1206 invoke_context.get_check_aligned(),
1207 )? = result_point;
1208 Ok(0)
1209 } else {
1210 Ok(1)
1211 }
1212 }
1213
1214 _ => Ok(1),
1215 }
1216 }
1217);
1218
1219declare_builtin_function!(
1220 SyscallSetReturnData,
1222 fn rust(
1223 invoke_context: &mut InvokeContext,
1224 addr: u64,
1225 len: u64,
1226 _arg3: u64,
1227 _arg4: u64,
1228 _arg5: u64,
1229 memory_mapping: &mut MemoryMapping,
1230 ) -> Result<u64, Error> {
1231 let budget = invoke_context.get_compute_budget();
1232
1233 let cost = len
1234 .checked_div(budget.cpi_bytes_per_unit)
1235 .unwrap_or(u64::MAX)
1236 .saturating_add(budget.syscall_base_cost);
1237 consume_compute_meter(invoke_context, cost)?;
1238
1239 if len > MAX_RETURN_DATA as u64 {
1240 return Err(SyscallError::ReturnDataTooLarge(len, MAX_RETURN_DATA as u64).into());
1241 }
1242
1243 let return_data = if len == 0 {
1244 Vec::new()
1245 } else {
1246 translate_slice::<u8>(
1247 memory_mapping,
1248 addr,
1249 len,
1250 invoke_context.get_check_aligned(),
1251 )?
1252 .to_vec()
1253 };
1254 let transaction_context = &mut invoke_context.transaction_context;
1255 let program_id = *transaction_context
1256 .get_current_instruction_context()
1257 .and_then(|instruction_context| {
1258 instruction_context.get_last_program_key(transaction_context)
1259 })?;
1260
1261 transaction_context.set_return_data(program_id, return_data)?;
1262
1263 Ok(0)
1264 }
1265);
1266
1267declare_builtin_function!(
1268 SyscallGetReturnData,
1270 fn rust(
1271 invoke_context: &mut InvokeContext,
1272 return_data_addr: u64,
1273 length: u64,
1274 program_id_addr: u64,
1275 _arg4: u64,
1276 _arg5: u64,
1277 memory_mapping: &mut MemoryMapping,
1278 ) -> Result<u64, Error> {
1279 let budget = invoke_context.get_compute_budget();
1280
1281 consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
1282
1283 let (program_id, return_data) = invoke_context.transaction_context.get_return_data();
1284 let length = length.min(return_data.len() as u64);
1285 if length != 0 {
1286 let cost = length
1287 .saturating_add(size_of::<Pubkey>() as u64)
1288 .checked_div(budget.cpi_bytes_per_unit)
1289 .unwrap_or(u64::MAX);
1290 consume_compute_meter(invoke_context, cost)?;
1291
1292 let return_data_result = translate_slice_mut::<u8>(
1293 memory_mapping,
1294 return_data_addr,
1295 length,
1296 invoke_context.get_check_aligned(),
1297 )?;
1298
1299 let to_slice = return_data_result;
1300 let from_slice = return_data
1301 .get(..length as usize)
1302 .ok_or(SyscallError::InvokeContextBorrowFailed)?;
1303 if to_slice.len() != from_slice.len() {
1304 return Err(SyscallError::InvalidLength.into());
1305 }
1306 to_slice.copy_from_slice(from_slice);
1307
1308 let program_id_result = translate_type_mut::<Pubkey>(
1309 memory_mapping,
1310 program_id_addr,
1311 invoke_context.get_check_aligned(),
1312 )?;
1313
1314 if !is_nonoverlapping(
1315 to_slice.as_ptr() as usize,
1316 length as usize,
1317 program_id_result as *const _ as usize,
1318 std::mem::size_of::<Pubkey>(),
1319 ) {
1320 return Err(SyscallError::CopyOverlapping.into());
1321 }
1322
1323 *program_id_result = *program_id;
1324 }
1325
1326 Ok(return_data.len() as u64)
1328 }
1329);
1330
1331declare_builtin_function!(
1332 SyscallGetProcessedSiblingInstruction,
1334 fn rust(
1335 invoke_context: &mut InvokeContext,
1336 index: u64,
1337 meta_addr: u64,
1338 program_id_addr: u64,
1339 data_addr: u64,
1340 accounts_addr: u64,
1341 memory_mapping: &mut MemoryMapping,
1342 ) -> Result<u64, Error> {
1343 let budget = invoke_context.get_compute_budget();
1344
1345 consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
1346
1347 let stack_height = invoke_context.get_stack_height();
1350 let instruction_trace_length = invoke_context
1351 .transaction_context
1352 .get_instruction_trace_length();
1353 let mut reverse_index_at_stack_height = 0;
1354 let mut found_instruction_context = None;
1355 for index_in_trace in (0..instruction_trace_length).rev() {
1356 let instruction_context = invoke_context
1357 .transaction_context
1358 .get_instruction_context_at_index_in_trace(index_in_trace)?;
1359 if instruction_context.get_stack_height() < stack_height {
1360 break;
1361 }
1362 if instruction_context.get_stack_height() == stack_height {
1363 if index.saturating_add(1) == reverse_index_at_stack_height {
1364 found_instruction_context = Some(instruction_context);
1365 break;
1366 }
1367 reverse_index_at_stack_height = reverse_index_at_stack_height.saturating_add(1);
1368 }
1369 }
1370
1371 if let Some(instruction_context) = found_instruction_context {
1372 let result_header = translate_type_mut::<ProcessedSiblingInstruction>(
1373 memory_mapping,
1374 meta_addr,
1375 invoke_context.get_check_aligned(),
1376 )?;
1377
1378 if result_header.data_len == (instruction_context.get_instruction_data().len() as u64)
1379 && result_header.accounts_len
1380 == (instruction_context.get_number_of_instruction_accounts() as u64)
1381 {
1382 let program_id = translate_type_mut::<Pubkey>(
1383 memory_mapping,
1384 program_id_addr,
1385 invoke_context.get_check_aligned(),
1386 )?;
1387 let data = translate_slice_mut::<u8>(
1388 memory_mapping,
1389 data_addr,
1390 result_header.data_len,
1391 invoke_context.get_check_aligned(),
1392 )?;
1393 let accounts = translate_slice_mut::<AccountMeta>(
1394 memory_mapping,
1395 accounts_addr,
1396 result_header.accounts_len,
1397 invoke_context.get_check_aligned(),
1398 )?;
1399
1400 if !is_nonoverlapping(
1401 result_header as *const _ as usize,
1402 std::mem::size_of::<ProcessedSiblingInstruction>(),
1403 program_id as *const _ as usize,
1404 std::mem::size_of::<Pubkey>(),
1405 ) || !is_nonoverlapping(
1406 result_header as *const _ as usize,
1407 std::mem::size_of::<ProcessedSiblingInstruction>(),
1408 accounts.as_ptr() as usize,
1409 std::mem::size_of::<AccountMeta>()
1410 .saturating_mul(result_header.accounts_len as usize),
1411 ) || !is_nonoverlapping(
1412 result_header as *const _ as usize,
1413 std::mem::size_of::<ProcessedSiblingInstruction>(),
1414 data.as_ptr() as usize,
1415 result_header.data_len as usize,
1416 ) || !is_nonoverlapping(
1417 program_id as *const _ as usize,
1418 std::mem::size_of::<Pubkey>(),
1419 data.as_ptr() as usize,
1420 result_header.data_len as usize,
1421 ) || !is_nonoverlapping(
1422 program_id as *const _ as usize,
1423 std::mem::size_of::<Pubkey>(),
1424 accounts.as_ptr() as usize,
1425 std::mem::size_of::<AccountMeta>()
1426 .saturating_mul(result_header.accounts_len as usize),
1427 ) || !is_nonoverlapping(
1428 data.as_ptr() as usize,
1429 result_header.data_len as usize,
1430 accounts.as_ptr() as usize,
1431 std::mem::size_of::<AccountMeta>()
1432 .saturating_mul(result_header.accounts_len as usize),
1433 ) {
1434 return Err(SyscallError::CopyOverlapping.into());
1435 }
1436
1437 *program_id = *instruction_context
1438 .get_last_program_key(invoke_context.transaction_context)?;
1439 data.clone_from_slice(instruction_context.get_instruction_data());
1440 let account_metas = (0..instruction_context.get_number_of_instruction_accounts())
1441 .map(|instruction_account_index| {
1442 Ok(AccountMeta {
1443 pubkey: *invoke_context
1444 .transaction_context
1445 .get_key_of_account_at_index(
1446 instruction_context
1447 .get_index_of_instruction_account_in_transaction(
1448 instruction_account_index,
1449 )?,
1450 )?,
1451 is_signer: instruction_context
1452 .is_instruction_account_signer(instruction_account_index)?,
1453 is_writable: instruction_context
1454 .is_instruction_account_writable(instruction_account_index)?,
1455 })
1456 })
1457 .collect::<Result<Vec<_>, InstructionError>>()?;
1458 accounts.clone_from_slice(account_metas.as_slice());
1459 }
1460 result_header.data_len = instruction_context.get_instruction_data().len() as u64;
1461 result_header.accounts_len =
1462 instruction_context.get_number_of_instruction_accounts() as u64;
1463 return Ok(true as u64);
1464 }
1465 Ok(false as u64)
1466 }
1467);
1468
1469declare_builtin_function!(
1470 SyscallGetStackHeight,
1472 fn rust(
1473 invoke_context: &mut InvokeContext,
1474 _arg1: u64,
1475 _arg2: u64,
1476 _arg3: u64,
1477 _arg4: u64,
1478 _arg5: u64,
1479 _memory_mapping: &mut MemoryMapping,
1480 ) -> Result<u64, Error> {
1481 let budget = invoke_context.get_compute_budget();
1482
1483 consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
1484
1485 Ok(invoke_context.get_stack_height() as u64)
1486 }
1487);
1488
1489declare_builtin_function!(
1490 SyscallAltBn128,
1492 fn rust(
1493 invoke_context: &mut InvokeContext,
1494 group_op: u64,
1495 input_addr: u64,
1496 input_size: u64,
1497 result_addr: u64,
1498 _arg5: u64,
1499 memory_mapping: &mut MemoryMapping,
1500 ) -> Result<u64, Error> {
1501 use miraland_sdk::alt_bn128::prelude::{ALT_BN128_ADD, ALT_BN128_MUL, ALT_BN128_PAIRING};
1502 let budget = invoke_context.get_compute_budget();
1503 let (cost, output): (u64, usize) = match group_op {
1504 ALT_BN128_ADD => (
1505 budget.alt_bn128_addition_cost,
1506 ALT_BN128_ADDITION_OUTPUT_LEN,
1507 ),
1508 ALT_BN128_MUL => (
1509 budget.alt_bn128_multiplication_cost,
1510 ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
1511 ),
1512 ALT_BN128_PAIRING => {
1513 let ele_len = input_size
1514 .checked_div(ALT_BN128_PAIRING_ELEMENT_LEN as u64)
1515 .expect("div by non-zero constant");
1516 let cost = budget
1517 .alt_bn128_pairing_one_pair_cost_first
1518 .saturating_add(
1519 budget
1520 .alt_bn128_pairing_one_pair_cost_other
1521 .saturating_mul(ele_len.saturating_sub(1)),
1522 )
1523 .saturating_add(budget.sha256_base_cost)
1524 .saturating_add(input_size)
1525 .saturating_add(ALT_BN128_PAIRING_OUTPUT_LEN as u64);
1526 (cost, ALT_BN128_PAIRING_OUTPUT_LEN)
1527 }
1528 _ => {
1529 return Err(SyscallError::InvalidAttribute.into());
1530 }
1531 };
1532
1533 consume_compute_meter(invoke_context, cost)?;
1534
1535 let input = translate_slice::<u8>(
1536 memory_mapping,
1537 input_addr,
1538 input_size,
1539 invoke_context.get_check_aligned(),
1540 )?;
1541
1542 let call_result = translate_slice_mut::<u8>(
1543 memory_mapping,
1544 result_addr,
1545 output as u64,
1546 invoke_context.get_check_aligned(),
1547 )?;
1548
1549 let calculation = match group_op {
1550 ALT_BN128_ADD => alt_bn128_addition,
1551 ALT_BN128_MUL => alt_bn128_multiplication,
1552 ALT_BN128_PAIRING => alt_bn128_pairing,
1553 _ => {
1554 return Err(SyscallError::InvalidAttribute.into());
1555 }
1556 };
1557
1558 let result_point = match calculation(input) {
1559 Ok(result_point) => result_point,
1560 Err(e) => {
1561 return Ok(e.into());
1562 }
1563 };
1564
1565 if result_point.len() != output {
1566 return Ok(AltBn128Error::SliceOutOfBounds.into());
1567 }
1568
1569 call_result.copy_from_slice(&result_point);
1570 Ok(SUCCESS)
1571 }
1572);
1573
1574declare_builtin_function!(
1575 SyscallBigModExp,
1577 fn rust(
1578 invoke_context: &mut InvokeContext,
1579 params: u64,
1580 return_value: u64,
1581 _arg3: u64,
1582 _arg4: u64,
1583 _arg5: u64,
1584 memory_mapping: &mut MemoryMapping,
1585 ) -> Result<u64, Error> {
1586 let params = &translate_slice::<BigModExpParams>(
1587 memory_mapping,
1588 params,
1589 1,
1590 invoke_context.get_check_aligned(),
1591 )?
1592 .first()
1593 .ok_or(SyscallError::InvalidLength)?;
1594
1595 if params.base_len > 512 || params.exponent_len > 512 || params.modulus_len > 512 {
1596 return Err(Box::new(SyscallError::InvalidLength));
1597 }
1598
1599 let input_len: u64 = std::cmp::max(params.base_len, params.exponent_len);
1600 let input_len: u64 = std::cmp::max(input_len, params.modulus_len);
1601
1602 let budget = invoke_context.get_compute_budget();
1603 consume_compute_meter(
1604 invoke_context,
1605 budget.syscall_base_cost.saturating_add(
1606 input_len
1607 .saturating_mul(input_len)
1608 .checked_div(budget.big_modular_exponentiation_cost)
1609 .unwrap_or(u64::MAX),
1610 ),
1611 )?;
1612
1613 let base = translate_slice::<u8>(
1614 memory_mapping,
1615 params.base as *const _ as u64,
1616 params.base_len,
1617 invoke_context.get_check_aligned(),
1618 )?;
1619
1620 let exponent = translate_slice::<u8>(
1621 memory_mapping,
1622 params.exponent as *const _ as u64,
1623 params.exponent_len,
1624 invoke_context.get_check_aligned(),
1625 )?;
1626
1627 let modulus = translate_slice::<u8>(
1628 memory_mapping,
1629 params.modulus as *const _ as u64,
1630 params.modulus_len,
1631 invoke_context.get_check_aligned(),
1632 )?;
1633
1634 let value = big_mod_exp(base, exponent, modulus);
1635
1636 let return_value = translate_slice_mut::<u8>(
1637 memory_mapping,
1638 return_value,
1639 params.modulus_len,
1640 invoke_context.get_check_aligned(),
1641 )?;
1642 return_value.copy_from_slice(value.as_slice());
1643
1644 Ok(0)
1645 }
1646);
1647
1648declare_builtin_function!(
1649 SyscallPoseidon,
1651 fn rust(
1652 invoke_context: &mut InvokeContext,
1653 parameters: u64,
1654 endianness: u64,
1655 vals_addr: u64,
1656 vals_len: u64,
1657 result_addr: u64,
1658 memory_mapping: &mut MemoryMapping,
1659 ) -> Result<u64, Error> {
1660 let parameters: poseidon::Parameters = parameters.try_into()?;
1661 let endianness: poseidon::Endianness = endianness.try_into()?;
1662
1663 if vals_len > 12 {
1664 ic_msg!(
1665 invoke_context,
1666 "Poseidon hashing {} sequences is not supported",
1667 vals_len,
1668 );
1669 return Err(SyscallError::InvalidLength.into());
1670 }
1671
1672 let budget = invoke_context.get_compute_budget();
1673 let Some(cost) = budget.poseidon_cost(vals_len) else {
1674 ic_msg!(
1675 invoke_context,
1676 "Overflow while calculating the compute cost"
1677 );
1678 return Err(SyscallError::ArithmeticOverflow.into());
1679 };
1680 consume_compute_meter(invoke_context, cost.to_owned())?;
1681
1682 let hash_result = translate_slice_mut::<u8>(
1683 memory_mapping,
1684 result_addr,
1685 poseidon::HASH_BYTES as u64,
1686 invoke_context.get_check_aligned(),
1687 )?;
1688 let inputs = translate_slice::<&[u8]>(
1689 memory_mapping,
1690 vals_addr,
1691 vals_len,
1692 invoke_context.get_check_aligned(),
1693 )?;
1694 let inputs = inputs
1695 .iter()
1696 .map(|input| {
1697 translate_slice::<u8>(
1698 memory_mapping,
1699 input.as_ptr() as *const _ as u64,
1700 input.len() as u64,
1701 invoke_context.get_check_aligned(),
1702 )
1703 })
1704 .collect::<Result<Vec<_>, Error>>()?;
1705 let hash = match poseidon::hashv(parameters, endianness, inputs.as_slice()) {
1706 Ok(hash) => hash,
1707 Err(e) => {
1708 return Ok(e.into());
1709 }
1710 };
1711 hash_result.copy_from_slice(&hash.to_bytes());
1712
1713 Ok(SUCCESS)
1714 }
1715);
1716
1717declare_builtin_function!(
1718 SyscallRemainingComputeUnits,
1720 fn rust(
1721 invoke_context: &mut InvokeContext,
1722 _arg1: u64,
1723 _arg2: u64,
1724 _arg3: u64,
1725 _arg4: u64,
1726 _arg5: u64,
1727 _memory_mapping: &mut MemoryMapping,
1728 ) -> Result<u64, Error> {
1729 let budget = invoke_context.get_compute_budget();
1730 consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
1731
1732 use solana_rbpf::vm::ContextObject;
1733 Ok(invoke_context.get_remaining())
1734 }
1735);
1736
1737declare_builtin_function!(
1738 SyscallAltBn128Compression,
1740 fn rust(
1741 invoke_context: &mut InvokeContext,
1742 op: u64,
1743 input_addr: u64,
1744 input_size: u64,
1745 result_addr: u64,
1746 _arg5: u64,
1747 memory_mapping: &mut MemoryMapping,
1748 ) -> Result<u64, Error> {
1749 use miraland_sdk::alt_bn128::compression::prelude::{
1750 alt_bn128_g1_compress, alt_bn128_g1_decompress, alt_bn128_g2_compress,
1751 alt_bn128_g2_decompress, ALT_BN128_G1_COMPRESS, ALT_BN128_G1_DECOMPRESS,
1752 ALT_BN128_G2_COMPRESS, ALT_BN128_G2_DECOMPRESS, G1, G1_COMPRESSED, G2, G2_COMPRESSED,
1753 };
1754 let budget = invoke_context.get_compute_budget();
1755 let base_cost = budget.syscall_base_cost;
1756 let (cost, output): (u64, usize) = match op {
1757 ALT_BN128_G1_COMPRESS => (
1758 base_cost.saturating_add(budget.alt_bn128_g1_compress),
1759 G1_COMPRESSED,
1760 ),
1761 ALT_BN128_G1_DECOMPRESS => {
1762 (base_cost.saturating_add(budget.alt_bn128_g1_decompress), G1)
1763 }
1764 ALT_BN128_G2_COMPRESS => (
1765 base_cost.saturating_add(budget.alt_bn128_g2_compress),
1766 G2_COMPRESSED,
1767 ),
1768 ALT_BN128_G2_DECOMPRESS => {
1769 (base_cost.saturating_add(budget.alt_bn128_g2_decompress), G2)
1770 }
1771 _ => {
1772 return Err(SyscallError::InvalidAttribute.into());
1773 }
1774 };
1775
1776 consume_compute_meter(invoke_context, cost)?;
1777
1778 let input = translate_slice::<u8>(
1779 memory_mapping,
1780 input_addr,
1781 input_size,
1782 invoke_context.get_check_aligned(),
1783 )?;
1784
1785 let call_result = translate_slice_mut::<u8>(
1786 memory_mapping,
1787 result_addr,
1788 output as u64,
1789 invoke_context.get_check_aligned(),
1790 )?;
1791
1792 match op {
1793 ALT_BN128_G1_COMPRESS => {
1794 let result_point = match alt_bn128_g1_compress(input) {
1795 Ok(result_point) => result_point,
1796 Err(e) => {
1797 return Ok(e.into());
1798 }
1799 };
1800 call_result.copy_from_slice(&result_point);
1801 Ok(SUCCESS)
1802 }
1803 ALT_BN128_G1_DECOMPRESS => {
1804 let result_point = match alt_bn128_g1_decompress(input) {
1805 Ok(result_point) => result_point,
1806 Err(e) => {
1807 return Ok(e.into());
1808 }
1809 };
1810 call_result.copy_from_slice(&result_point);
1811 Ok(SUCCESS)
1812 }
1813 ALT_BN128_G2_COMPRESS => {
1814 let result_point = match alt_bn128_g2_compress(input) {
1815 Ok(result_point) => result_point,
1816 Err(e) => {
1817 return Ok(e.into());
1818 }
1819 };
1820 call_result.copy_from_slice(&result_point);
1821 Ok(SUCCESS)
1822 }
1823 ALT_BN128_G2_DECOMPRESS => {
1824 let result_point = match alt_bn128_g2_decompress(input) {
1825 Ok(result_point) => result_point,
1826 Err(e) => {
1827 return Ok(e.into());
1828 }
1829 };
1830 call_result.copy_from_slice(&result_point);
1831 Ok(SUCCESS)
1832 }
1833 _ => Err(SyscallError::InvalidAttribute.into()),
1834 }
1835 }
1836);
1837
1838declare_builtin_function!(
1839 SyscallHash<H: HasherImpl>,
1841 fn rust(
1842 invoke_context: &mut InvokeContext,
1843 vals_addr: u64,
1844 vals_len: u64,
1845 result_addr: u64,
1846 _arg4: u64,
1847 _arg5: u64,
1848 memory_mapping: &mut MemoryMapping,
1849 ) -> Result<u64, Error> {
1850 let compute_budget = invoke_context.get_compute_budget();
1851 let hash_base_cost = H::get_base_cost(compute_budget);
1852 let hash_byte_cost = H::get_byte_cost(compute_budget);
1853 let hash_max_slices = H::get_max_slices(compute_budget);
1854 if hash_max_slices < vals_len {
1855 ic_msg!(
1856 invoke_context,
1857 "{} Hashing {} sequences in one syscall is over the limit {}",
1858 H::NAME,
1859 vals_len,
1860 hash_max_slices,
1861 );
1862 return Err(SyscallError::TooManySlices.into());
1863 }
1864
1865 consume_compute_meter(invoke_context, hash_base_cost)?;
1866
1867 let hash_result = translate_slice_mut::<u8>(
1868 memory_mapping,
1869 result_addr,
1870 std::mem::size_of::<H::Output>() as u64,
1871 invoke_context.get_check_aligned(),
1872 )?;
1873 let mut hasher = H::create_hasher();
1874 if vals_len > 0 {
1875 let vals = translate_slice::<&[u8]>(
1876 memory_mapping,
1877 vals_addr,
1878 vals_len,
1879 invoke_context.get_check_aligned(),
1880 )?;
1881 for val in vals.iter() {
1882 let bytes = translate_slice::<u8>(
1883 memory_mapping,
1884 val.as_ptr() as u64,
1885 val.len() as u64,
1886 invoke_context.get_check_aligned(),
1887 )?;
1888 let cost = compute_budget.mem_op_base_cost.max(
1889 hash_byte_cost.saturating_mul(
1890 (val.len() as u64)
1891 .checked_div(2)
1892 .expect("div by non-zero literal"),
1893 ),
1894 );
1895 consume_compute_meter(invoke_context, cost)?;
1896 hasher.hash(bytes);
1897 }
1898 }
1899 hash_result.copy_from_slice(hasher.result().as_ref());
1900 Ok(0)
1901 }
1902);
1903
1904#[cfg(test)]
1905#[allow(clippy::arithmetic_side_effects)]
1906#[allow(clippy::indexing_slicing)]
1907mod tests {
1908 #[allow(deprecated)]
1909 use miraland_sdk::sysvar::fees::Fees;
1910 use {
1911 super::*,
1912 crate::mock_create_vm,
1913 assert_matches::assert_matches,
1914 core::slice,
1915 miraland_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
1916 solana_rbpf::{
1917 error::EbpfError, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1918 },
1919 miraland_sdk::{
1920 account::{create_account_shared_data_for_test, AccountSharedData},
1921 bpf_loader,
1922 fee_calculator::FeeCalculator,
1923 hash::{hashv, HASH_BYTES},
1924 instruction::Instruction,
1925 program::check_type_assumptions,
1926 stable_layout::stable_instruction::StableInstruction,
1927 sysvar::{
1928 self, clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule,
1929 },
1930 },
1931 std::{mem, str::FromStr},
1932 };
1933
1934 macro_rules! assert_access_violation {
1935 ($result:expr, $va:expr, $len:expr) => {
1936 match $result.unwrap_err().downcast_ref::<EbpfError>().unwrap() {
1937 EbpfError::AccessViolation(_, va, len, _) if $va == *va && $len == *len => {}
1938 EbpfError::StackAccessViolation(_, va, len, _) if $va == *va && $len == *len => {}
1939 _ => panic!(),
1940 }
1941 };
1942 }
1943
1944 macro_rules! prepare_mockup {
1945 ($invoke_context:ident,
1946 $program_key:ident,
1947 $loader_key:expr $(,)?) => {
1948 let $program_key = Pubkey::new_unique();
1949 let transaction_accounts = vec![
1950 (
1951 $loader_key,
1952 AccountSharedData::new(0, 0, &native_loader::id()),
1953 ),
1954 ($program_key, AccountSharedData::new(0, 0, &$loader_key)),
1955 ];
1956 with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
1957 $invoke_context
1958 .transaction_context
1959 .get_next_instruction_context()
1960 .unwrap()
1961 .configure(&[0, 1], &[], &[]);
1962 $invoke_context.push().unwrap();
1963 };
1964 }
1965
1966 #[allow(dead_code)]
1967 struct MockSlice {
1968 pub vm_addr: u64,
1969 pub len: usize,
1970 }
1971
1972 #[test]
1973 fn test_translate() {
1974 const START: u64 = 0x100000000;
1975 const LENGTH: u64 = 1000;
1976
1977 let data = vec![0u8; LENGTH as usize];
1978 let addr = data.as_ptr() as u64;
1979 let config = Config::default();
1980 let memory_mapping = MemoryMapping::new(
1981 vec![MemoryRegion::new_readonly(&data, START)],
1982 &config,
1983 &SBPFVersion::V2,
1984 )
1985 .unwrap();
1986
1987 let cases = vec![
1988 (true, START, 0, addr),
1989 (true, START, 1, addr),
1990 (true, START, LENGTH, addr),
1991 (true, START + 1, LENGTH - 1, addr + 1),
1992 (false, START + 1, LENGTH, 0),
1993 (true, START + LENGTH - 1, 1, addr + LENGTH - 1),
1994 (true, START + LENGTH, 0, addr + LENGTH),
1995 (false, START + LENGTH, 1, 0),
1996 (false, START, LENGTH + 1, 0),
1997 (false, 0, 0, 0),
1998 (false, 0, 1, 0),
1999 (false, START - 1, 0, 0),
2000 (false, START - 1, 1, 0),
2001 (true, START + LENGTH / 2, LENGTH / 2, addr + LENGTH / 2),
2002 ];
2003 for (ok, start, length, value) in cases {
2004 if ok {
2005 assert_eq!(
2006 translate(&memory_mapping, AccessType::Load, start, length).unwrap(),
2007 value
2008 )
2009 } else {
2010 assert!(translate(&memory_mapping, AccessType::Load, start, length).is_err())
2011 }
2012 }
2013 }
2014
2015 #[test]
2016 fn test_translate_type() {
2017 let config = Config::default();
2018
2019 let pubkey = miraland_sdk::pubkey::new_rand();
2021 let memory_mapping = MemoryMapping::new(
2022 vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
2023 &config,
2024 &SBPFVersion::V2,
2025 )
2026 .unwrap();
2027 let translated_pubkey =
2028 translate_type::<Pubkey>(&memory_mapping, 0x100000000, true).unwrap();
2029 assert_eq!(pubkey, *translated_pubkey);
2030
2031 let instruction = Instruction::new_with_bincode(
2033 miraland_sdk::pubkey::new_rand(),
2034 &"foobar",
2035 vec![AccountMeta::new(miraland_sdk::pubkey::new_rand(), false)],
2036 );
2037 let instruction = StableInstruction::from(instruction);
2038 let memory_region = MemoryRegion::new_readonly(bytes_of(&instruction), 0x100000000);
2039 let memory_mapping =
2040 MemoryMapping::new(vec![memory_region], &config, &SBPFVersion::V2).unwrap();
2041 let translated_instruction =
2042 translate_type::<StableInstruction>(&memory_mapping, 0x100000000, true).unwrap();
2043 assert_eq!(instruction, *translated_instruction);
2044
2045 let memory_region = MemoryRegion::new_readonly(&bytes_of(&instruction)[..1], 0x100000000);
2046 let memory_mapping =
2047 MemoryMapping::new(vec![memory_region], &config, &SBPFVersion::V2).unwrap();
2048 assert!(translate_type::<Instruction>(&memory_mapping, 0x100000000, true).is_err());
2049 }
2050
2051 #[test]
2052 fn test_translate_slice() {
2053 let config = Config::default();
2054
2055 let good_data = vec![1u8, 2, 3, 4, 5];
2057 let data: Vec<u8> = vec![];
2058 assert_eq!(0x1 as *const u8, data.as_ptr());
2059 let memory_mapping = MemoryMapping::new(
2060 vec![MemoryRegion::new_readonly(&good_data, 0x100000000)],
2061 &config,
2062 &SBPFVersion::V2,
2063 )
2064 .unwrap();
2065 let translated_data =
2066 translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, 0, true).unwrap();
2067 assert_eq!(data, translated_data);
2068 assert_eq!(0, translated_data.len());
2069
2070 let mut data = vec![1u8, 2, 3, 4, 5];
2072 let memory_mapping = MemoryMapping::new(
2073 vec![MemoryRegion::new_readonly(&data, 0x100000000)],
2074 &config,
2075 &SBPFVersion::V2,
2076 )
2077 .unwrap();
2078 let translated_data =
2079 translate_slice::<u8>(&memory_mapping, 0x100000000, data.len() as u64, true).unwrap();
2080 assert_eq!(data, translated_data);
2081 *data.first_mut().unwrap() = 10;
2082 assert_eq!(data, translated_data);
2083 assert!(
2084 translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, u64::MAX, true).is_err()
2085 );
2086
2087 assert!(
2088 translate_slice::<u8>(&memory_mapping, 0x100000000 - 1, data.len() as u64, true,)
2089 .is_err()
2090 );
2091
2092 let mut data = vec![1u64, 2, 3, 4, 5];
2094 let memory_mapping = MemoryMapping::new(
2095 vec![MemoryRegion::new_readonly(
2096 bytes_of_slice(&data),
2097 0x100000000,
2098 )],
2099 &config,
2100 &SBPFVersion::V2,
2101 )
2102 .unwrap();
2103 let translated_data =
2104 translate_slice::<u64>(&memory_mapping, 0x100000000, data.len() as u64, true).unwrap();
2105 assert_eq!(data, translated_data);
2106 *data.first_mut().unwrap() = 10;
2107 assert_eq!(data, translated_data);
2108 assert!(translate_slice::<u64>(&memory_mapping, 0x100000000, u64::MAX, true).is_err());
2109
2110 let mut data = vec![miraland_sdk::pubkey::new_rand(); 5];
2112 let memory_mapping = MemoryMapping::new(
2113 vec![MemoryRegion::new_readonly(
2114 unsafe {
2115 slice::from_raw_parts(data.as_ptr() as *const u8, mem::size_of::<Pubkey>() * 5)
2116 },
2117 0x100000000,
2118 )],
2119 &config,
2120 &SBPFVersion::V2,
2121 )
2122 .unwrap();
2123 let translated_data =
2124 translate_slice::<Pubkey>(&memory_mapping, 0x100000000, data.len() as u64, true)
2125 .unwrap();
2126 assert_eq!(data, translated_data);
2127 *data.first_mut().unwrap() = miraland_sdk::pubkey::new_rand(); assert_eq!(data, translated_data);
2129 }
2130
2131 #[test]
2132 fn test_translate_string_and_do() {
2133 let string = "Gaggablaghblagh!";
2134 let config = Config::default();
2135 let memory_mapping = MemoryMapping::new(
2136 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2137 &config,
2138 &SBPFVersion::V2,
2139 )
2140 .unwrap();
2141 assert_eq!(
2142 42,
2143 translate_string_and_do(
2144 &memory_mapping,
2145 0x100000000,
2146 string.len() as u64,
2147 true,
2148 &mut |string: &str| {
2149 assert_eq!(string, "Gaggablaghblagh!");
2150 Ok(42)
2151 }
2152 )
2153 .unwrap()
2154 );
2155 }
2156
2157 #[test]
2158 #[should_panic(expected = "Abort")]
2159 fn test_syscall_abort() {
2160 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2161 let config = Config::default();
2162 let mut memory_mapping = MemoryMapping::new(vec![], &config, &SBPFVersion::V2).unwrap();
2163 let result = SyscallAbort::rust(&mut invoke_context, 0, 0, 0, 0, 0, &mut memory_mapping);
2164 result.unwrap();
2165 }
2166
2167 #[test]
2168 #[should_panic(expected = "Panic(\"Gaggablaghblagh!\", 42, 84)")]
2169 fn test_syscall_sol_panic() {
2170 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2171
2172 let string = "Gaggablaghblagh!";
2173 let config = Config::default();
2174 let mut memory_mapping = MemoryMapping::new(
2175 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2176 &config,
2177 &SBPFVersion::V2,
2178 )
2179 .unwrap();
2180
2181 invoke_context.mock_set_remaining(string.len() as u64 - 1);
2182 let result = SyscallPanic::rust(
2183 &mut invoke_context,
2184 0x100000000,
2185 string.len() as u64,
2186 42,
2187 84,
2188 0,
2189 &mut memory_mapping,
2190 );
2191 assert_matches!(
2192 result,
2193 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2194 );
2195
2196 invoke_context.mock_set_remaining(string.len() as u64);
2197 let result = SyscallPanic::rust(
2198 &mut invoke_context,
2199 0x100000000,
2200 string.len() as u64,
2201 42,
2202 84,
2203 0,
2204 &mut memory_mapping,
2205 );
2206 result.unwrap();
2207 }
2208
2209 #[test]
2210 fn test_syscall_sol_log() {
2211 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2212
2213 let string = "Gaggablaghblagh!";
2214 let config = Config::default();
2215 let mut memory_mapping = MemoryMapping::new(
2216 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2217 &config,
2218 &SBPFVersion::V2,
2219 )
2220 .unwrap();
2221
2222 invoke_context.mock_set_remaining(400 - 1);
2223 let result = SyscallLog::rust(
2224 &mut invoke_context,
2225 0x100000001, string.len() as u64,
2227 0,
2228 0,
2229 0,
2230 &mut memory_mapping,
2231 );
2232 assert_access_violation!(result, 0x100000001, string.len() as u64);
2233 let result = SyscallLog::rust(
2234 &mut invoke_context,
2235 0x100000000,
2236 string.len() as u64 * 2, 0,
2238 0,
2239 0,
2240 &mut memory_mapping,
2241 );
2242 assert_access_violation!(result, 0x100000000, string.len() as u64 * 2);
2243
2244 let result = SyscallLog::rust(
2245 &mut invoke_context,
2246 0x100000000,
2247 string.len() as u64,
2248 0,
2249 0,
2250 0,
2251 &mut memory_mapping,
2252 );
2253 result.unwrap();
2254 let result = SyscallLog::rust(
2255 &mut invoke_context,
2256 0x100000000,
2257 string.len() as u64,
2258 0,
2259 0,
2260 0,
2261 &mut memory_mapping,
2262 );
2263 assert_matches!(
2264 result,
2265 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2266 );
2267
2268 assert_eq!(
2269 invoke_context
2270 .get_log_collector()
2271 .unwrap()
2272 .borrow()
2273 .get_recorded_content(),
2274 &["Program log: Gaggablaghblagh!".to_string()]
2275 );
2276 }
2277
2278 #[test]
2279 fn test_syscall_sol_log_u64() {
2280 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2281 let cost = invoke_context.get_compute_budget().log_64_units;
2282
2283 invoke_context.mock_set_remaining(cost);
2284 let config = Config::default();
2285 let mut memory_mapping = MemoryMapping::new(vec![], &config, &SBPFVersion::V2).unwrap();
2286 let result = SyscallLogU64::rust(&mut invoke_context, 1, 2, 3, 4, 5, &mut memory_mapping);
2287 result.unwrap();
2288
2289 assert_eq!(
2290 invoke_context
2291 .get_log_collector()
2292 .unwrap()
2293 .borrow()
2294 .get_recorded_content(),
2295 &["Program log: 0x1, 0x2, 0x3, 0x4, 0x5".to_string()]
2296 );
2297 }
2298
2299 #[test]
2300 fn test_syscall_sol_pubkey() {
2301 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2302 let cost = invoke_context.get_compute_budget().log_pubkey_units;
2303
2304 let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap();
2305 let config = Config::default();
2306 let mut memory_mapping = MemoryMapping::new(
2307 vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
2308 &config,
2309 &SBPFVersion::V2,
2310 )
2311 .unwrap();
2312
2313 let result = SyscallLogPubkey::rust(
2314 &mut invoke_context,
2315 0x100000001, 32,
2317 0,
2318 0,
2319 0,
2320 &mut memory_mapping,
2321 );
2322 assert_access_violation!(result, 0x100000001, 32);
2323
2324 invoke_context.mock_set_remaining(1);
2325 let result =
2326 SyscallLogPubkey::rust(&mut invoke_context, 100, 32, 0, 0, 0, &mut memory_mapping);
2327 assert_matches!(
2328 result,
2329 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2330 );
2331
2332 invoke_context.mock_set_remaining(cost);
2333 let result = SyscallLogPubkey::rust(
2334 &mut invoke_context,
2335 0x100000000,
2336 0,
2337 0,
2338 0,
2339 0,
2340 &mut memory_mapping,
2341 );
2342 result.unwrap();
2343
2344 assert_eq!(
2345 invoke_context
2346 .get_log_collector()
2347 .unwrap()
2348 .borrow()
2349 .get_recorded_content(),
2350 &["Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN".to_string()]
2351 );
2352 }
2353
2354 #[test]
2355 fn test_syscall_sol_alloc_free() {
2356 {
2358 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2359 mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
2360 let mut vm = vm.unwrap();
2361 let invoke_context = &mut vm.context_object_pointer;
2362 let memory_mapping = &mut vm.memory_mapping;
2363 let result = SyscallAllocFree::rust(
2364 invoke_context,
2365 miraland_sdk::entrypoint::HEAP_LENGTH as u64,
2366 0,
2367 0,
2368 0,
2369 0,
2370 memory_mapping,
2371 );
2372 assert_ne!(result.unwrap(), 0);
2373 let result = SyscallAllocFree::rust(
2374 invoke_context,
2375 miraland_sdk::entrypoint::HEAP_LENGTH as u64,
2376 0,
2377 0,
2378 0,
2379 0,
2380 memory_mapping,
2381 );
2382 assert_eq!(result.unwrap(), 0);
2383 let result =
2384 SyscallAllocFree::rust(invoke_context, u64::MAX, 0, 0, 0, 0, memory_mapping);
2385 assert_eq!(result.unwrap(), 0);
2386 }
2387
2388 {
2390 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2391 invoke_context.feature_set = Arc::new(FeatureSet::default());
2392 mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
2393 let mut vm = vm.unwrap();
2394 let invoke_context = &mut vm.context_object_pointer;
2395 let memory_mapping = &mut vm.memory_mapping;
2396 for _ in 0..100 {
2397 let result = SyscallAllocFree::rust(invoke_context, 1, 0, 0, 0, 0, memory_mapping);
2398 assert_ne!(result.unwrap(), 0);
2399 }
2400 let result = SyscallAllocFree::rust(
2401 invoke_context,
2402 miraland_sdk::entrypoint::HEAP_LENGTH as u64,
2403 0,
2404 0,
2405 0,
2406 0,
2407 memory_mapping,
2408 );
2409 assert_eq!(result.unwrap(), 0);
2410 }
2411
2412 {
2414 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2415 mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
2416 let mut vm = vm.unwrap();
2417 let invoke_context = &mut vm.context_object_pointer;
2418 let memory_mapping = &mut vm.memory_mapping;
2419 for _ in 0..12 {
2420 let result = SyscallAllocFree::rust(invoke_context, 1, 0, 0, 0, 0, memory_mapping);
2421 assert_ne!(result.unwrap(), 0);
2422 }
2423 let result = SyscallAllocFree::rust(
2424 invoke_context,
2425 miraland_sdk::entrypoint::HEAP_LENGTH as u64,
2426 0,
2427 0,
2428 0,
2429 0,
2430 memory_mapping,
2431 );
2432 assert_eq!(result.unwrap(), 0);
2433 }
2434
2435 fn aligned<T>() {
2438 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2439 mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
2440 let mut vm = vm.unwrap();
2441 let invoke_context = &mut vm.context_object_pointer;
2442 let memory_mapping = &mut vm.memory_mapping;
2443 let result = SyscallAllocFree::rust(
2444 invoke_context,
2445 size_of::<T>() as u64,
2446 0,
2447 0,
2448 0,
2449 0,
2450 memory_mapping,
2451 );
2452 let address = result.unwrap();
2453 assert_ne!(address, 0);
2454 assert!(address_is_aligned::<T>(address));
2455 }
2456 aligned::<u8>();
2457 aligned::<u16>();
2458 aligned::<u32>();
2459 aligned::<u64>();
2460 aligned::<u128>();
2461 }
2462
2463 #[test]
2464 fn test_syscall_sha256() {
2465 let config = Config::default();
2466 prepare_mockup!(invoke_context, program_id, bpf_loader_deprecated::id());
2467
2468 let bytes1 = "Gaggablaghblagh!";
2469 let bytes2 = "flurbos";
2470
2471 let mock_slice1 = MockSlice {
2472 vm_addr: 0x300000000,
2473 len: bytes1.len(),
2474 };
2475 let mock_slice2 = MockSlice {
2476 vm_addr: 0x400000000,
2477 len: bytes2.len(),
2478 };
2479 let bytes_to_hash = [mock_slice1, mock_slice2];
2480 let mut hash_result = [0; HASH_BYTES];
2481 let ro_len = bytes_to_hash.len() as u64;
2482 let ro_va = 0x100000000;
2483 let rw_va = 0x200000000;
2484 let mut memory_mapping = MemoryMapping::new(
2485 vec![
2486 MemoryRegion::new_readonly(bytes_of_slice(&bytes_to_hash), ro_va),
2487 MemoryRegion::new_writable(bytes_of_slice_mut(&mut hash_result), rw_va),
2488 MemoryRegion::new_readonly(bytes1.as_bytes(), bytes_to_hash[0].vm_addr),
2489 MemoryRegion::new_readonly(bytes2.as_bytes(), bytes_to_hash[1].vm_addr),
2490 ],
2491 &config,
2492 &SBPFVersion::V2,
2493 )
2494 .unwrap();
2495
2496 invoke_context.mock_set_remaining(
2497 (invoke_context.get_compute_budget().sha256_base_cost
2498 + invoke_context.get_compute_budget().mem_op_base_cost.max(
2499 invoke_context
2500 .get_compute_budget()
2501 .sha256_byte_cost
2502 .saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2),
2503 ))
2504 * 4,
2505 );
2506
2507 let result = SyscallHash::rust::<Sha256Hasher>(
2508 &mut invoke_context,
2509 ro_va,
2510 ro_len,
2511 rw_va,
2512 0,
2513 0,
2514 &mut memory_mapping,
2515 );
2516 result.unwrap();
2517
2518 let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes();
2519 assert_eq!(hash_result, hash_local);
2520 let result = SyscallHash::rust::<Sha256Hasher>(
2521 &mut invoke_context,
2522 ro_va - 1, ro_len,
2524 rw_va,
2525 0,
2526 0,
2527 &mut memory_mapping,
2528 );
2529 assert_access_violation!(result, ro_va - 1, 32);
2530 let result = SyscallHash::rust::<Sha256Hasher>(
2531 &mut invoke_context,
2532 ro_va,
2533 ro_len + 1, rw_va,
2535 0,
2536 0,
2537 &mut memory_mapping,
2538 );
2539 assert_access_violation!(result, ro_va, 48);
2540 let result = SyscallHash::rust::<Sha256Hasher>(
2541 &mut invoke_context,
2542 ro_va,
2543 ro_len,
2544 rw_va - 1, 0,
2546 0,
2547 &mut memory_mapping,
2548 );
2549 assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64);
2550 let result = SyscallHash::rust::<Sha256Hasher>(
2551 &mut invoke_context,
2552 ro_va,
2553 ro_len,
2554 rw_va,
2555 0,
2556 0,
2557 &mut memory_mapping,
2558 );
2559 assert_matches!(
2560 result,
2561 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2562 );
2563 }
2564
2565 #[test]
2566 fn test_syscall_edwards_curve_point_validation() {
2567 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_EDWARDS;
2568
2569 let config = Config::default();
2570 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2571
2572 let valid_bytes: [u8; 32] = [
2573 201, 179, 241, 122, 180, 185, 239, 50, 183, 52, 221, 0, 153, 195, 43, 18, 22, 38, 187,
2574 206, 179, 192, 210, 58, 53, 45, 150, 98, 89, 17, 158, 11,
2575 ];
2576 let valid_bytes_va = 0x100000000;
2577
2578 let invalid_bytes: [u8; 32] = [
2579 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2580 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2581 ];
2582 let invalid_bytes_va = 0x200000000;
2583
2584 let mut memory_mapping = MemoryMapping::new(
2585 vec![
2586 MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
2587 MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
2588 ],
2589 &config,
2590 &SBPFVersion::V2,
2591 )
2592 .unwrap();
2593
2594 invoke_context.mock_set_remaining(
2595 (invoke_context
2596 .get_compute_budget()
2597 .curve25519_edwards_validate_point_cost)
2598 * 2,
2599 );
2600
2601 let result = SyscallCurvePointValidation::rust(
2602 &mut invoke_context,
2603 CURVE25519_EDWARDS,
2604 valid_bytes_va,
2605 0,
2606 0,
2607 0,
2608 &mut memory_mapping,
2609 );
2610 assert_eq!(0, result.unwrap());
2611
2612 let result = SyscallCurvePointValidation::rust(
2613 &mut invoke_context,
2614 CURVE25519_EDWARDS,
2615 invalid_bytes_va,
2616 0,
2617 0,
2618 0,
2619 &mut memory_mapping,
2620 );
2621 assert_eq!(1, result.unwrap());
2622
2623 let result = SyscallCurvePointValidation::rust(
2624 &mut invoke_context,
2625 CURVE25519_EDWARDS,
2626 valid_bytes_va,
2627 0,
2628 0,
2629 0,
2630 &mut memory_mapping,
2631 );
2632 assert_matches!(
2633 result,
2634 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2635 );
2636 }
2637
2638 #[test]
2639 fn test_syscall_ristretto_curve_point_validation() {
2640 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_RISTRETTO;
2641
2642 let config = Config::default();
2643 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2644
2645 let valid_bytes: [u8; 32] = [
2646 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11,
2647 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118,
2648 ];
2649 let valid_bytes_va = 0x100000000;
2650
2651 let invalid_bytes: [u8; 32] = [
2652 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2653 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2654 ];
2655 let invalid_bytes_va = 0x200000000;
2656
2657 let mut memory_mapping = MemoryMapping::new(
2658 vec![
2659 MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
2660 MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
2661 ],
2662 &config,
2663 &SBPFVersion::V2,
2664 )
2665 .unwrap();
2666
2667 invoke_context.mock_set_remaining(
2668 (invoke_context
2669 .get_compute_budget()
2670 .curve25519_ristretto_validate_point_cost)
2671 * 2,
2672 );
2673
2674 let result = SyscallCurvePointValidation::rust(
2675 &mut invoke_context,
2676 CURVE25519_RISTRETTO,
2677 valid_bytes_va,
2678 0,
2679 0,
2680 0,
2681 &mut memory_mapping,
2682 );
2683 assert_eq!(0, result.unwrap());
2684
2685 let result = SyscallCurvePointValidation::rust(
2686 &mut invoke_context,
2687 CURVE25519_RISTRETTO,
2688 invalid_bytes_va,
2689 0,
2690 0,
2691 0,
2692 &mut memory_mapping,
2693 );
2694 assert_eq!(1, result.unwrap());
2695
2696 let result = SyscallCurvePointValidation::rust(
2697 &mut invoke_context,
2698 CURVE25519_RISTRETTO,
2699 valid_bytes_va,
2700 0,
2701 0,
2702 0,
2703 &mut memory_mapping,
2704 );
2705 assert_matches!(
2706 result,
2707 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2708 );
2709 }
2710
2711 #[test]
2712 fn test_syscall_edwards_curve_group_ops() {
2713 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::{
2714 ADD, CURVE25519_EDWARDS, MUL, SUB,
2715 };
2716
2717 let config = Config::default();
2718 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2719
2720 let left_point: [u8; 32] = [
2721 33, 124, 71, 170, 117, 69, 151, 247, 59, 12, 95, 125, 133, 166, 64, 5, 2, 27, 90, 27,
2722 200, 167, 59, 164, 52, 54, 52, 200, 29, 13, 34, 213,
2723 ];
2724 let left_point_va = 0x100000000;
2725 let right_point: [u8; 32] = [
2726 70, 222, 137, 221, 253, 204, 71, 51, 78, 8, 124, 1, 67, 200, 102, 225, 122, 228, 111,
2727 183, 129, 14, 131, 210, 212, 95, 109, 246, 55, 10, 159, 91,
2728 ];
2729 let right_point_va = 0x200000000;
2730 let scalar: [u8; 32] = [
2731 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
2732 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
2733 ];
2734 let scalar_va = 0x300000000;
2735 let invalid_point: [u8; 32] = [
2736 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2737 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2738 ];
2739 let invalid_point_va = 0x400000000;
2740 let mut result_point: [u8; 32] = [0; 32];
2741 let result_point_va = 0x500000000;
2742
2743 let mut memory_mapping = MemoryMapping::new(
2744 vec![
2745 MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
2746 MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
2747 MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
2748 MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
2749 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
2750 ],
2751 &config,
2752 &SBPFVersion::V2,
2753 )
2754 .unwrap();
2755
2756 invoke_context.mock_set_remaining(
2757 (invoke_context
2758 .get_compute_budget()
2759 .curve25519_edwards_add_cost
2760 + invoke_context
2761 .get_compute_budget()
2762 .curve25519_edwards_subtract_cost
2763 + invoke_context
2764 .get_compute_budget()
2765 .curve25519_edwards_multiply_cost)
2766 * 2,
2767 );
2768
2769 let result = SyscallCurveGroupOps::rust(
2770 &mut invoke_context,
2771 CURVE25519_EDWARDS,
2772 ADD,
2773 left_point_va,
2774 right_point_va,
2775 result_point_va,
2776 &mut memory_mapping,
2777 );
2778
2779 assert_eq!(0, result.unwrap());
2780 let expected_sum = [
2781 7, 251, 187, 86, 186, 232, 57, 242, 193, 236, 49, 200, 90, 29, 254, 82, 46, 80, 83, 70,
2782 244, 153, 23, 156, 2, 138, 207, 51, 165, 38, 200, 85,
2783 ];
2784 assert_eq!(expected_sum, result_point);
2785
2786 let result = SyscallCurveGroupOps::rust(
2787 &mut invoke_context,
2788 CURVE25519_EDWARDS,
2789 ADD,
2790 invalid_point_va,
2791 right_point_va,
2792 result_point_va,
2793 &mut memory_mapping,
2794 );
2795 assert_eq!(1, result.unwrap());
2796
2797 let result = SyscallCurveGroupOps::rust(
2798 &mut invoke_context,
2799 CURVE25519_EDWARDS,
2800 SUB,
2801 left_point_va,
2802 right_point_va,
2803 result_point_va,
2804 &mut memory_mapping,
2805 );
2806
2807 assert_eq!(0, result.unwrap());
2808 let expected_difference = [
2809 60, 87, 90, 68, 232, 25, 7, 172, 247, 120, 158, 104, 52, 127, 94, 244, 5, 79, 253, 15,
2810 48, 69, 82, 134, 155, 70, 188, 81, 108, 95, 212, 9,
2811 ];
2812 assert_eq!(expected_difference, result_point);
2813
2814 let result = SyscallCurveGroupOps::rust(
2815 &mut invoke_context,
2816 CURVE25519_EDWARDS,
2817 SUB,
2818 invalid_point_va,
2819 right_point_va,
2820 result_point_va,
2821 &mut memory_mapping,
2822 );
2823 assert_eq!(1, result.unwrap());
2824
2825 let result = SyscallCurveGroupOps::rust(
2826 &mut invoke_context,
2827 CURVE25519_EDWARDS,
2828 MUL,
2829 scalar_va,
2830 right_point_va,
2831 result_point_va,
2832 &mut memory_mapping,
2833 );
2834
2835 result.unwrap();
2836 let expected_product = [
2837 64, 150, 40, 55, 80, 49, 217, 209, 105, 229, 181, 65, 241, 68, 2, 106, 220, 234, 211,
2838 71, 159, 76, 156, 114, 242, 68, 147, 31, 243, 211, 191, 124,
2839 ];
2840 assert_eq!(expected_product, result_point);
2841
2842 let result = SyscallCurveGroupOps::rust(
2843 &mut invoke_context,
2844 CURVE25519_EDWARDS,
2845 MUL,
2846 scalar_va,
2847 invalid_point_va,
2848 result_point_va,
2849 &mut memory_mapping,
2850 );
2851 assert_eq!(1, result.unwrap());
2852
2853 let result = SyscallCurveGroupOps::rust(
2854 &mut invoke_context,
2855 CURVE25519_EDWARDS,
2856 MUL,
2857 scalar_va,
2858 invalid_point_va,
2859 result_point_va,
2860 &mut memory_mapping,
2861 );
2862 assert_matches!(
2863 result,
2864 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2865 );
2866 }
2867
2868 #[test]
2869 fn test_syscall_ristretto_curve_group_ops() {
2870 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::{
2871 ADD, CURVE25519_RISTRETTO, MUL, SUB,
2872 };
2873
2874 let config = Config::default();
2875 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2876
2877 let left_point: [u8; 32] = [
2878 208, 165, 125, 204, 2, 100, 218, 17, 170, 194, 23, 9, 102, 156, 134, 136, 217, 190, 98,
2879 34, 183, 194, 228, 153, 92, 11, 108, 103, 28, 57, 88, 15,
2880 ];
2881 let left_point_va = 0x100000000;
2882 let right_point: [u8; 32] = [
2883 208, 241, 72, 163, 73, 53, 32, 174, 54, 194, 71, 8, 70, 181, 244, 199, 93, 147, 99,
2884 231, 162, 127, 25, 40, 39, 19, 140, 132, 112, 212, 145, 108,
2885 ];
2886 let right_point_va = 0x200000000;
2887 let scalar: [u8; 32] = [
2888 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
2889 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
2890 ];
2891 let scalar_va = 0x300000000;
2892 let invalid_point: [u8; 32] = [
2893 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2894 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2895 ];
2896 let invalid_point_va = 0x400000000;
2897 let mut result_point: [u8; 32] = [0; 32];
2898 let result_point_va = 0x500000000;
2899
2900 let mut memory_mapping = MemoryMapping::new(
2901 vec![
2902 MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
2903 MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
2904 MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
2905 MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
2906 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
2907 ],
2908 &config,
2909 &SBPFVersion::V2,
2910 )
2911 .unwrap();
2912
2913 invoke_context.mock_set_remaining(
2914 (invoke_context
2915 .get_compute_budget()
2916 .curve25519_ristretto_add_cost
2917 + invoke_context
2918 .get_compute_budget()
2919 .curve25519_ristretto_subtract_cost
2920 + invoke_context
2921 .get_compute_budget()
2922 .curve25519_ristretto_multiply_cost)
2923 * 2,
2924 );
2925
2926 let result = SyscallCurveGroupOps::rust(
2927 &mut invoke_context,
2928 CURVE25519_RISTRETTO,
2929 ADD,
2930 left_point_va,
2931 right_point_va,
2932 result_point_va,
2933 &mut memory_mapping,
2934 );
2935
2936 assert_eq!(0, result.unwrap());
2937 let expected_sum = [
2938 78, 173, 9, 241, 180, 224, 31, 107, 176, 210, 144, 240, 118, 73, 70, 191, 128, 119,
2939 141, 113, 125, 215, 161, 71, 49, 176, 87, 38, 180, 177, 39, 78,
2940 ];
2941 assert_eq!(expected_sum, result_point);
2942
2943 let result = SyscallCurveGroupOps::rust(
2944 &mut invoke_context,
2945 CURVE25519_RISTRETTO,
2946 ADD,
2947 invalid_point_va,
2948 right_point_va,
2949 result_point_va,
2950 &mut memory_mapping,
2951 );
2952 assert_eq!(1, result.unwrap());
2953
2954 let result = SyscallCurveGroupOps::rust(
2955 &mut invoke_context,
2956 CURVE25519_RISTRETTO,
2957 SUB,
2958 left_point_va,
2959 right_point_va,
2960 result_point_va,
2961 &mut memory_mapping,
2962 );
2963
2964 assert_eq!(0, result.unwrap());
2965 let expected_difference = [
2966 150, 72, 222, 61, 148, 79, 96, 130, 151, 176, 29, 217, 231, 211, 0, 215, 76, 86, 212,
2967 146, 110, 128, 24, 151, 187, 144, 108, 233, 221, 208, 157, 52,
2968 ];
2969 assert_eq!(expected_difference, result_point);
2970
2971 let result = SyscallCurveGroupOps::rust(
2972 &mut invoke_context,
2973 CURVE25519_RISTRETTO,
2974 SUB,
2975 invalid_point_va,
2976 right_point_va,
2977 result_point_va,
2978 &mut memory_mapping,
2979 );
2980
2981 assert_eq!(1, result.unwrap());
2982
2983 let result = SyscallCurveGroupOps::rust(
2984 &mut invoke_context,
2985 CURVE25519_RISTRETTO,
2986 MUL,
2987 scalar_va,
2988 right_point_va,
2989 result_point_va,
2990 &mut memory_mapping,
2991 );
2992
2993 result.unwrap();
2994 let expected_product = [
2995 4, 16, 46, 2, 53, 151, 201, 133, 117, 149, 232, 164, 119, 109, 136, 20, 153, 24, 124,
2996 21, 101, 124, 80, 19, 119, 100, 77, 108, 65, 187, 228, 5,
2997 ];
2998 assert_eq!(expected_product, result_point);
2999
3000 let result = SyscallCurveGroupOps::rust(
3001 &mut invoke_context,
3002 CURVE25519_RISTRETTO,
3003 MUL,
3004 scalar_va,
3005 invalid_point_va,
3006 result_point_va,
3007 &mut memory_mapping,
3008 );
3009
3010 assert_eq!(1, result.unwrap());
3011
3012 let result = SyscallCurveGroupOps::rust(
3013 &mut invoke_context,
3014 CURVE25519_RISTRETTO,
3015 MUL,
3016 scalar_va,
3017 invalid_point_va,
3018 result_point_va,
3019 &mut memory_mapping,
3020 );
3021 assert_matches!(
3022 result,
3023 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
3024 );
3025 }
3026
3027 #[test]
3028 fn test_syscall_multiscalar_multiplication() {
3029 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::{
3030 CURVE25519_EDWARDS, CURVE25519_RISTRETTO,
3031 };
3032
3033 let config = Config::default();
3034 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3035
3036 let scalar_a: [u8; 32] = [
3037 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3038 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3039 ];
3040 let scalar_b: [u8; 32] = [
3041 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3042 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3043 ];
3044
3045 let scalars = [scalar_a, scalar_b];
3046 let scalars_va = 0x100000000;
3047
3048 let edwards_point_x: [u8; 32] = [
3049 252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3050 53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3051 ];
3052 let edwards_point_y: [u8; 32] = [
3053 10, 111, 8, 236, 97, 189, 124, 69, 89, 176, 222, 39, 199, 253, 111, 11, 248, 186, 128,
3054 90, 120, 128, 248, 210, 232, 183, 93, 104, 111, 150, 7, 241,
3055 ];
3056 let edwards_points = [edwards_point_x, edwards_point_y];
3057 let edwards_points_va = 0x200000000;
3058
3059 let ristretto_point_x: [u8; 32] = [
3060 130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3061 179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3062 ];
3063 let ristretto_point_y: [u8; 32] = [
3064 152, 156, 155, 197, 152, 232, 92, 206, 219, 159, 193, 134, 121, 128, 139, 36, 56, 191,
3065 51, 143, 72, 204, 87, 76, 110, 124, 101, 96, 238, 158, 42, 108,
3066 ];
3067 let ristretto_points = [ristretto_point_x, ristretto_point_y];
3068 let ristretto_points_va = 0x300000000;
3069
3070 let mut result_point: [u8; 32] = [0; 32];
3071 let result_point_va = 0x400000000;
3072
3073 let mut memory_mapping = MemoryMapping::new(
3074 vec![
3075 MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3076 MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3077 MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3078 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3079 ],
3080 &config,
3081 &SBPFVersion::V2,
3082 )
3083 .unwrap();
3084
3085 invoke_context.mock_set_remaining(
3086 invoke_context
3087 .get_compute_budget()
3088 .curve25519_edwards_msm_base_cost
3089 + invoke_context
3090 .get_compute_budget()
3091 .curve25519_edwards_msm_incremental_cost
3092 + invoke_context
3093 .get_compute_budget()
3094 .curve25519_ristretto_msm_base_cost
3095 + invoke_context
3096 .get_compute_budget()
3097 .curve25519_ristretto_msm_incremental_cost,
3098 );
3099
3100 let result = SyscallCurveMultiscalarMultiplication::rust(
3101 &mut invoke_context,
3102 CURVE25519_EDWARDS,
3103 scalars_va,
3104 edwards_points_va,
3105 2,
3106 result_point_va,
3107 &mut memory_mapping,
3108 );
3109
3110 assert_eq!(0, result.unwrap());
3111 let expected_product = [
3112 30, 174, 168, 34, 160, 70, 63, 166, 236, 18, 74, 144, 185, 222, 208, 243, 5, 54, 223,
3113 172, 185, 75, 244, 26, 70, 18, 248, 46, 207, 184, 235, 60,
3114 ];
3115 assert_eq!(expected_product, result_point);
3116
3117 let result = SyscallCurveMultiscalarMultiplication::rust(
3118 &mut invoke_context,
3119 CURVE25519_RISTRETTO,
3120 scalars_va,
3121 ristretto_points_va,
3122 2,
3123 result_point_va,
3124 &mut memory_mapping,
3125 );
3126
3127 assert_eq!(0, result.unwrap());
3128 let expected_product = [
3129 78, 120, 86, 111, 152, 64, 146, 84, 14, 236, 77, 147, 237, 190, 251, 241, 136, 167, 21,
3130 94, 84, 118, 92, 140, 120, 81, 30, 246, 173, 140, 195, 86,
3131 ];
3132 assert_eq!(expected_product, result_point);
3133 }
3134
3135 #[test]
3136 fn test_syscall_multiscalar_multiplication_maximum_length_exceeded() {
3137 use miraland_zk_token_sdk::curve25519::curve_syscall_traits::{
3138 CURVE25519_EDWARDS, CURVE25519_RISTRETTO,
3139 };
3140
3141 let config = Config::default();
3142 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3143
3144 let scalar: [u8; 32] = [
3145 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3146 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3147 ];
3148 let scalars = [scalar; 513];
3149 let scalars_va = 0x100000000;
3150
3151 let edwards_point: [u8; 32] = [
3152 252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3153 53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3154 ];
3155 let edwards_points = [edwards_point; 513];
3156 let edwards_points_va = 0x200000000;
3157
3158 let ristretto_point: [u8; 32] = [
3159 130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3160 179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3161 ];
3162 let ristretto_points = [ristretto_point; 513];
3163 let ristretto_points_va = 0x300000000;
3164
3165 let mut result_point: [u8; 32] = [0; 32];
3166 let result_point_va = 0x400000000;
3167
3168 let mut memory_mapping = MemoryMapping::new(
3169 vec![
3170 MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3171 MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3172 MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3173 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3174 ],
3175 &config,
3176 &SBPFVersion::V2,
3177 )
3178 .unwrap();
3179
3180 invoke_context.mock_set_remaining(500_000);
3182 let result = SyscallCurveMultiscalarMultiplication::rust(
3183 &mut invoke_context,
3184 CURVE25519_EDWARDS,
3185 scalars_va,
3186 edwards_points_va,
3187 512, result_point_va,
3189 &mut memory_mapping,
3190 );
3191
3192 assert_eq!(0, result.unwrap());
3193 let expected_product = [
3194 20, 146, 226, 37, 22, 61, 86, 249, 208, 40, 38, 11, 126, 101, 10, 82, 81, 77, 88, 209,
3195 15, 76, 82, 251, 180, 133, 84, 243, 162, 0, 11, 145,
3196 ];
3197 assert_eq!(expected_product, result_point);
3198
3199 invoke_context.mock_set_remaining(500_000);
3200 let result = SyscallCurveMultiscalarMultiplication::rust(
3201 &mut invoke_context,
3202 CURVE25519_EDWARDS,
3203 scalars_va,
3204 edwards_points_va,
3205 513, result_point_va,
3207 &mut memory_mapping,
3208 )
3209 .unwrap_err()
3210 .downcast::<SyscallError>()
3211 .unwrap();
3212
3213 assert_eq!(*result, SyscallError::InvalidLength);
3214
3215 invoke_context.mock_set_remaining(500_000);
3217 let result = SyscallCurveMultiscalarMultiplication::rust(
3218 &mut invoke_context,
3219 CURVE25519_RISTRETTO,
3220 scalars_va,
3221 ristretto_points_va,
3222 512, result_point_va,
3224 &mut memory_mapping,
3225 );
3226
3227 assert_eq!(0, result.unwrap());
3228 let expected_product = [
3229 146, 224, 127, 193, 252, 64, 196, 181, 246, 104, 27, 116, 183, 52, 200, 239, 2, 108,
3230 21, 27, 97, 44, 95, 65, 26, 218, 223, 39, 197, 132, 51, 49,
3231 ];
3232 assert_eq!(expected_product, result_point);
3233
3234 invoke_context.mock_set_remaining(500_000);
3235 let result = SyscallCurveMultiscalarMultiplication::rust(
3236 &mut invoke_context,
3237 CURVE25519_RISTRETTO,
3238 scalars_va,
3239 ristretto_points_va,
3240 513, result_point_va,
3242 &mut memory_mapping,
3243 )
3244 .unwrap_err()
3245 .downcast::<SyscallError>()
3246 .unwrap();
3247
3248 assert_eq!(*result, SyscallError::InvalidLength);
3249 }
3250
3251 fn create_filled_type<T: Default>(zero_init: bool) -> T {
3252 let mut val = T::default();
3253 let p = &mut val as *mut _ as *mut u8;
3254 for i in 0..(size_of::<T>() as isize) {
3255 unsafe {
3256 *p.offset(i) = if zero_init { 0 } else { i as u8 };
3257 }
3258 }
3259 val
3260 }
3261
3262 fn are_bytes_equal<T>(first: &T, second: &T) -> bool {
3263 let p_first = first as *const _ as *const u8;
3264 let p_second = second as *const _ as *const u8;
3265 for i in 0..(size_of::<T>() as isize) {
3266 unsafe {
3267 if *p_first.offset(i) != *p_second.offset(i) {
3268 return false;
3269 }
3270 }
3271 }
3272 true
3273 }
3274
3275 #[test]
3276 #[allow(deprecated)]
3277 fn test_syscall_get_sysvar() {
3278 let config = Config::default();
3279
3280 let mut src_clock = create_filled_type::<Clock>(false);
3281 src_clock.slot = 1;
3282 src_clock.epoch_start_timestamp = 2;
3283 src_clock.epoch = 3;
3284 src_clock.leader_schedule_epoch = 4;
3285 src_clock.unix_timestamp = 5;
3286 let mut src_epochschedule = create_filled_type::<EpochSchedule>(false);
3287 src_epochschedule.slots_per_epoch = 1;
3288 src_epochschedule.leader_schedule_slot_offset = 2;
3289 src_epochschedule.warmup = false;
3290 src_epochschedule.first_normal_epoch = 3;
3291 src_epochschedule.first_normal_slot = 4;
3292 let mut src_fees = create_filled_type::<Fees>(false);
3293 src_fees.fee_calculator = FeeCalculator {
3294 lamports_per_signature: 1,
3295 };
3296 let mut src_rent = create_filled_type::<Rent>(false);
3297 src_rent.lamports_per_byte_year = 1;
3298 src_rent.exemption_threshold = 2.0;
3299 src_rent.burn_percent = 3;
3300
3301 let mut src_rewards = create_filled_type::<EpochRewards>(false);
3302 src_rewards.total_rewards = 100;
3303 src_rewards.distributed_rewards = 10;
3304 src_rewards.distribution_complete_block_height = 42;
3305
3306 let mut sysvar_cache = SysvarCache::default();
3307 sysvar_cache.set_clock(src_clock.clone());
3308 sysvar_cache.set_epoch_schedule(src_epochschedule.clone());
3309 sysvar_cache.set_fees(src_fees.clone());
3310 sysvar_cache.set_rent(src_rent.clone());
3311 sysvar_cache.set_epoch_rewards(src_rewards);
3312
3313 let transaction_accounts = vec![
3314 (
3315 sysvar::clock::id(),
3316 create_account_shared_data_for_test(&src_clock),
3317 ),
3318 (
3319 sysvar::epoch_schedule::id(),
3320 create_account_shared_data_for_test(&src_epochschedule),
3321 ),
3322 (
3323 sysvar::fees::id(),
3324 create_account_shared_data_for_test(&src_fees),
3325 ),
3326 (
3327 sysvar::rent::id(),
3328 create_account_shared_data_for_test(&src_rent),
3329 ),
3330 (
3331 sysvar::epoch_rewards::id(),
3332 create_account_shared_data_for_test(&src_rewards),
3333 ),
3334 ];
3335 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
3336
3337 {
3339 let mut got_clock = Clock::default();
3340 let got_clock_va = 0x100000000;
3341
3342 let mut memory_mapping = MemoryMapping::new(
3343 vec![MemoryRegion::new_writable(
3344 bytes_of_mut(&mut got_clock),
3345 got_clock_va,
3346 )],
3347 &config,
3348 &SBPFVersion::V2,
3349 )
3350 .unwrap();
3351
3352 let result = SyscallGetClockSysvar::rust(
3353 &mut invoke_context,
3354 got_clock_va,
3355 0,
3356 0,
3357 0,
3358 0,
3359 &mut memory_mapping,
3360 );
3361 result.unwrap();
3362 assert_eq!(got_clock, src_clock);
3363
3364 let mut clean_clock = create_filled_type::<Clock>(true);
3365 clean_clock.slot = src_clock.slot;
3366 clean_clock.epoch_start_timestamp = src_clock.epoch_start_timestamp;
3367 clean_clock.epoch = src_clock.epoch;
3368 clean_clock.leader_schedule_epoch = src_clock.leader_schedule_epoch;
3369 clean_clock.unix_timestamp = src_clock.unix_timestamp;
3370 assert!(are_bytes_equal(&got_clock, &clean_clock));
3371 }
3372
3373 {
3375 let mut got_epochschedule = EpochSchedule::default();
3376 let got_epochschedule_va = 0x100000000;
3377
3378 let mut memory_mapping = MemoryMapping::new(
3379 vec![MemoryRegion::new_writable(
3380 bytes_of_mut(&mut got_epochschedule),
3381 got_epochschedule_va,
3382 )],
3383 &config,
3384 &SBPFVersion::V2,
3385 )
3386 .unwrap();
3387
3388 let result = SyscallGetEpochScheduleSysvar::rust(
3389 &mut invoke_context,
3390 got_epochschedule_va,
3391 0,
3392 0,
3393 0,
3394 0,
3395 &mut memory_mapping,
3396 );
3397 result.unwrap();
3398 assert_eq!(got_epochschedule, src_epochschedule);
3399
3400 let mut clean_epochschedule = create_filled_type::<EpochSchedule>(true);
3401 clean_epochschedule.slots_per_epoch = src_epochschedule.slots_per_epoch;
3402 clean_epochschedule.leader_schedule_slot_offset =
3403 src_epochschedule.leader_schedule_slot_offset;
3404 clean_epochschedule.warmup = src_epochschedule.warmup;
3405 clean_epochschedule.first_normal_epoch = src_epochschedule.first_normal_epoch;
3406 clean_epochschedule.first_normal_slot = src_epochschedule.first_normal_slot;
3407 assert!(are_bytes_equal(&got_epochschedule, &clean_epochschedule));
3408 }
3409
3410 {
3412 let mut got_fees = Fees::default();
3413 let got_fees_va = 0x100000000;
3414
3415 let mut memory_mapping = MemoryMapping::new(
3416 vec![MemoryRegion::new_writable(
3417 bytes_of_mut(&mut got_fees),
3418 got_fees_va,
3419 )],
3420 &config,
3421 &SBPFVersion::V2,
3422 )
3423 .unwrap();
3424
3425 let result = SyscallGetFeesSysvar::rust(
3426 &mut invoke_context,
3427 got_fees_va,
3428 0,
3429 0,
3430 0,
3431 0,
3432 &mut memory_mapping,
3433 );
3434 result.unwrap();
3435 assert_eq!(got_fees, src_fees);
3436
3437 let mut clean_fees = create_filled_type::<Fees>(true);
3438 clean_fees.fee_calculator = src_fees.fee_calculator;
3439 assert!(are_bytes_equal(&got_fees, &clean_fees));
3440 }
3441
3442 {
3444 let mut got_rent = create_filled_type::<Rent>(true);
3445 let got_rent_va = 0x100000000;
3446
3447 let mut memory_mapping = MemoryMapping::new(
3448 vec![MemoryRegion::new_writable(
3449 bytes_of_mut(&mut got_rent),
3450 got_rent_va,
3451 )],
3452 &config,
3453 &SBPFVersion::V2,
3454 )
3455 .unwrap();
3456
3457 let result = SyscallGetRentSysvar::rust(
3458 &mut invoke_context,
3459 got_rent_va,
3460 0,
3461 0,
3462 0,
3463 0,
3464 &mut memory_mapping,
3465 );
3466 result.unwrap();
3467 assert_eq!(got_rent, src_rent);
3468
3469 let mut clean_rent = create_filled_type::<Rent>(true);
3470 clean_rent.lamports_per_byte_year = src_rent.lamports_per_byte_year;
3471 clean_rent.exemption_threshold = src_rent.exemption_threshold;
3472 clean_rent.burn_percent = src_rent.burn_percent;
3473 assert!(are_bytes_equal(&got_rent, &clean_rent));
3474 }
3475
3476 {
3478 let mut got_rewards = create_filled_type::<EpochRewards>(true);
3479 let got_rewards_va = 0x100000000;
3480
3481 let mut memory_mapping = MemoryMapping::new(
3482 vec![MemoryRegion::new_writable(
3483 bytes_of_mut(&mut got_rewards),
3484 got_rewards_va,
3485 )],
3486 &config,
3487 &SBPFVersion::V2,
3488 )
3489 .unwrap();
3490
3491 let result = SyscallGetEpochRewardsSysvar::rust(
3492 &mut invoke_context,
3493 got_rewards_va,
3494 0,
3495 0,
3496 0,
3497 0,
3498 &mut memory_mapping,
3499 );
3500 result.unwrap();
3501 assert_eq!(got_rewards, src_rewards);
3502
3503 let mut clean_rewards = create_filled_type::<EpochRewards>(true);
3504 clean_rewards.total_rewards = src_rewards.total_rewards;
3505 clean_rewards.distributed_rewards = src_rewards.distributed_rewards;
3506 clean_rewards.distribution_complete_block_height =
3507 src_rewards.distribution_complete_block_height;
3508 assert!(are_bytes_equal(&got_rewards, &clean_rewards));
3509 }
3510 }
3511
3512 type BuiltinFunctionRustInterface<'a> = fn(
3513 &mut InvokeContext<'a>,
3514 u64,
3515 u64,
3516 u64,
3517 u64,
3518 u64,
3519 &mut MemoryMapping,
3520 ) -> Result<u64, Box<dyn std::error::Error>>;
3521
3522 fn call_program_address_common<'a, 'b: 'a>(
3523 invoke_context: &'a mut InvokeContext<'b>,
3524 seeds: &[&[u8]],
3525 program_id: &Pubkey,
3526 overlap_outputs: bool,
3527 syscall: BuiltinFunctionRustInterface<'b>,
3528 ) -> Result<(Pubkey, u8), Error> {
3529 const SEEDS_VA: u64 = 0x100000000;
3530 const PROGRAM_ID_VA: u64 = 0x200000000;
3531 const ADDRESS_VA: u64 = 0x300000000;
3532 const BUMP_SEED_VA: u64 = 0x400000000;
3533 const SEED_VA: u64 = 0x500000000;
3534
3535 let config = Config::default();
3536 let mut address = Pubkey::default();
3537 let mut bump_seed = 0;
3538 let mut regions = vec![
3539 MemoryRegion::new_readonly(bytes_of(program_id), PROGRAM_ID_VA),
3540 MemoryRegion::new_writable(bytes_of_mut(&mut address), ADDRESS_VA),
3541 MemoryRegion::new_writable(bytes_of_mut(&mut bump_seed), BUMP_SEED_VA),
3542 ];
3543
3544 let mut mock_slices = Vec::with_capacity(seeds.len());
3545 for (i, seed) in seeds.iter().enumerate() {
3546 let vm_addr = SEED_VA.saturating_add((i as u64).saturating_mul(0x100000000));
3547 let mock_slice = MockSlice {
3548 vm_addr,
3549 len: seed.len(),
3550 };
3551 mock_slices.push(mock_slice);
3552 regions.push(MemoryRegion::new_readonly(bytes_of_slice(seed), vm_addr));
3553 }
3554 regions.push(MemoryRegion::new_readonly(
3555 bytes_of_slice(&mock_slices),
3556 SEEDS_VA,
3557 ));
3558 let mut memory_mapping = MemoryMapping::new(regions, &config, &SBPFVersion::V2).unwrap();
3559
3560 let result = syscall(
3561 invoke_context,
3562 SEEDS_VA,
3563 seeds.len() as u64,
3564 PROGRAM_ID_VA,
3565 ADDRESS_VA,
3566 if overlap_outputs {
3567 ADDRESS_VA
3568 } else {
3569 BUMP_SEED_VA
3570 },
3571 &mut memory_mapping,
3572 );
3573 result.map(|_| (address, bump_seed))
3574 }
3575
3576 fn create_program_address(
3577 invoke_context: &mut InvokeContext,
3578 seeds: &[&[u8]],
3579 address: &Pubkey,
3580 ) -> Result<Pubkey, Error> {
3581 let (address, _) = call_program_address_common(
3582 invoke_context,
3583 seeds,
3584 address,
3585 false,
3586 SyscallCreateProgramAddress::rust,
3587 )?;
3588 Ok(address)
3589 }
3590
3591 fn try_find_program_address(
3592 invoke_context: &mut InvokeContext,
3593 seeds: &[&[u8]],
3594 address: &Pubkey,
3595 ) -> Result<(Pubkey, u8), Error> {
3596 call_program_address_common(
3597 invoke_context,
3598 seeds,
3599 address,
3600 false,
3601 SyscallTryFindProgramAddress::rust,
3602 )
3603 }
3604
3605 #[test]
3606 fn test_set_and_get_return_data() {
3607 const SRC_VA: u64 = 0x100000000;
3608 const DST_VA: u64 = 0x200000000;
3609 const PROGRAM_ID_VA: u64 = 0x300000000;
3610 let data = vec![42; 24];
3611 let mut data_buffer = vec![0; 16];
3612 let mut id_buffer = vec![0; 32];
3613
3614 let config = Config::default();
3615 let mut memory_mapping = MemoryMapping::new(
3616 vec![
3617 MemoryRegion::new_readonly(&data, SRC_VA),
3618 MemoryRegion::new_writable(&mut data_buffer, DST_VA),
3619 MemoryRegion::new_writable(&mut id_buffer, PROGRAM_ID_VA),
3620 ],
3621 &config,
3622 &SBPFVersion::V2,
3623 )
3624 .unwrap();
3625
3626 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3627
3628 let result = SyscallSetReturnData::rust(
3629 &mut invoke_context,
3630 SRC_VA,
3631 data.len() as u64,
3632 0,
3633 0,
3634 0,
3635 &mut memory_mapping,
3636 );
3637 assert_eq!(result.unwrap(), 0);
3638
3639 let result = SyscallGetReturnData::rust(
3640 &mut invoke_context,
3641 DST_VA,
3642 data_buffer.len() as u64,
3643 PROGRAM_ID_VA,
3644 0,
3645 0,
3646 &mut memory_mapping,
3647 );
3648 assert_eq!(result.unwrap() as usize, data.len());
3649 assert_eq!(data.get(0..data_buffer.len()).unwrap(), data_buffer);
3650 assert_eq!(id_buffer, program_id.to_bytes());
3651
3652 let result = SyscallGetReturnData::rust(
3653 &mut invoke_context,
3654 PROGRAM_ID_VA,
3655 data_buffer.len() as u64,
3656 PROGRAM_ID_VA,
3657 0,
3658 0,
3659 &mut memory_mapping,
3660 );
3661 assert_matches!(
3662 result,
3663 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
3664 );
3665 }
3666
3667 #[test]
3668 fn test_syscall_sol_get_processed_sibling_instruction() {
3669 let transaction_accounts = (0..9)
3670 .map(|_| {
3671 (
3672 Pubkey::new_unique(),
3673 AccountSharedData::new(0, 0, &bpf_loader::id()),
3674 )
3675 })
3676 .collect::<Vec<_>>();
3677 let instruction_trace = [1, 2, 3, 2, 2, 3, 4, 3];
3678 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
3679 for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
3680 while stack_height
3681 <= invoke_context
3682 .transaction_context
3683 .get_instruction_context_stack_height()
3684 {
3685 invoke_context.transaction_context.pop().unwrap();
3686 }
3687 if stack_height
3688 > invoke_context
3689 .transaction_context
3690 .get_instruction_context_stack_height()
3691 {
3692 let instruction_accounts = [InstructionAccount {
3693 index_in_transaction: index_in_trace.saturating_add(1) as IndexOfAccount,
3694 index_in_caller: 0, index_in_callee: 0,
3696 is_signer: false,
3697 is_writable: false,
3698 }];
3699 invoke_context
3700 .transaction_context
3701 .get_next_instruction_context()
3702 .unwrap()
3703 .configure(&[0], &instruction_accounts, &[index_in_trace as u8]);
3704 invoke_context.transaction_context.push().unwrap();
3705 }
3706 }
3707
3708 let syscall_base_cost = invoke_context.get_compute_budget().syscall_base_cost;
3709
3710 const VM_BASE_ADDRESS: u64 = 0x100000000;
3711 const META_OFFSET: usize = 0;
3712 const PROGRAM_ID_OFFSET: usize =
3713 META_OFFSET + std::mem::size_of::<ProcessedSiblingInstruction>();
3714 const DATA_OFFSET: usize = PROGRAM_ID_OFFSET + std::mem::size_of::<Pubkey>();
3715 const ACCOUNTS_OFFSET: usize = DATA_OFFSET + 0x100;
3716 const END_OFFSET: usize = ACCOUNTS_OFFSET + std::mem::size_of::<AccountInfo>() * 4;
3717 let mut memory = [0u8; END_OFFSET];
3718 let config = Config::default();
3719 let mut memory_mapping = MemoryMapping::new(
3720 vec![MemoryRegion::new_writable(&mut memory, VM_BASE_ADDRESS)],
3721 &config,
3722 &SBPFVersion::V2,
3723 )
3724 .unwrap();
3725 let processed_sibling_instruction = translate_type_mut::<ProcessedSiblingInstruction>(
3726 &memory_mapping,
3727 VM_BASE_ADDRESS,
3728 true,
3729 )
3730 .unwrap();
3731 processed_sibling_instruction.data_len = 1;
3732 processed_sibling_instruction.accounts_len = 1;
3733 let program_id = translate_type_mut::<Pubkey>(
3734 &memory_mapping,
3735 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
3736 true,
3737 )
3738 .unwrap();
3739 let data = translate_slice_mut::<u8>(
3740 &memory_mapping,
3741 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
3742 processed_sibling_instruction.data_len,
3743 true,
3744 )
3745 .unwrap();
3746 let accounts = translate_slice_mut::<AccountMeta>(
3747 &memory_mapping,
3748 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
3749 processed_sibling_instruction.accounts_len,
3750 true,
3751 )
3752 .unwrap();
3753
3754 invoke_context.mock_set_remaining(syscall_base_cost);
3755 let result = SyscallGetProcessedSiblingInstruction::rust(
3756 &mut invoke_context,
3757 0,
3758 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3759 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
3760 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
3761 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
3762 &mut memory_mapping,
3763 );
3764 assert_eq!(result.unwrap(), 1);
3765 {
3766 let transaction_context = &invoke_context.transaction_context;
3767 assert_eq!(processed_sibling_instruction.data_len, 1);
3768 assert_eq!(processed_sibling_instruction.accounts_len, 1);
3769 assert_eq!(
3770 program_id,
3771 transaction_context.get_key_of_account_at_index(0).unwrap(),
3772 );
3773 assert_eq!(data, &[5]);
3774 assert_eq!(
3775 accounts,
3776 &[AccountMeta {
3777 pubkey: *transaction_context.get_key_of_account_at_index(6).unwrap(),
3778 is_signer: false,
3779 is_writable: false
3780 }]
3781 );
3782 }
3783
3784 invoke_context.mock_set_remaining(syscall_base_cost);
3785 let result = SyscallGetProcessedSiblingInstruction::rust(
3786 &mut invoke_context,
3787 1,
3788 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3789 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
3790 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
3791 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
3792 &mut memory_mapping,
3793 );
3794 assert_eq!(result.unwrap(), 0);
3795
3796 invoke_context.mock_set_remaining(syscall_base_cost);
3797 let result = SyscallGetProcessedSiblingInstruction::rust(
3798 &mut invoke_context,
3799 0,
3800 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3801 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3802 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3803 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
3804 &mut memory_mapping,
3805 );
3806 assert_matches!(
3807 result,
3808 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
3809 );
3810 }
3811
3812 #[test]
3813 fn test_create_program_address() {
3814 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3817 let address = bpf_loader_upgradeable::id();
3818
3819 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
3820 assert_matches!(
3821 create_program_address(&mut invoke_context, &[exceeded_seed], &address),
3822 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
3823 );
3824 assert_matches!(
3825 create_program_address(
3826 &mut invoke_context,
3827 &[b"short_seed", exceeded_seed],
3828 &address,
3829 ),
3830 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
3831 );
3832 let max_seed = &[0; MAX_SEED_LEN];
3833 assert!(create_program_address(&mut invoke_context, &[max_seed], &address).is_ok());
3834 let exceeded_seeds: &[&[u8]] = &[
3835 &[1],
3836 &[2],
3837 &[3],
3838 &[4],
3839 &[5],
3840 &[6],
3841 &[7],
3842 &[8],
3843 &[9],
3844 &[10],
3845 &[11],
3846 &[12],
3847 &[13],
3848 &[14],
3849 &[15],
3850 &[16],
3851 ];
3852 assert!(create_program_address(&mut invoke_context, exceeded_seeds, &address).is_ok());
3853 let max_seeds: &[&[u8]] = &[
3854 &[1],
3855 &[2],
3856 &[3],
3857 &[4],
3858 &[5],
3859 &[6],
3860 &[7],
3861 &[8],
3862 &[9],
3863 &[10],
3864 &[11],
3865 &[12],
3866 &[13],
3867 &[14],
3868 &[15],
3869 &[16],
3870 &[17],
3871 ];
3872 assert_matches!(
3873 create_program_address(&mut invoke_context, max_seeds, &address),
3874 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
3875 );
3876 assert_eq!(
3877 create_program_address(&mut invoke_context, &[b"", &[1]], &address).unwrap(),
3878 "BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
3879 .parse()
3880 .unwrap(),
3881 );
3882 assert_eq!(
3883 create_program_address(&mut invoke_context, &["☉".as_ref(), &[0]], &address).unwrap(),
3884 "13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
3885 .parse()
3886 .unwrap(),
3887 );
3888 assert_eq!(
3889 create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
3890 .unwrap(),
3891 "2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
3892 .parse()
3893 .unwrap(),
3894 );
3895 let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
3896 assert_eq!(
3897 create_program_address(&mut invoke_context, &[public_key.as_ref(), &[1]], &address)
3898 .unwrap(),
3899 "976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
3900 .parse()
3901 .unwrap(),
3902 );
3903 assert_ne!(
3904 create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
3905 .unwrap(),
3906 create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(),
3907 );
3908 invoke_context.mock_set_remaining(0);
3909 assert_matches!(
3910 create_program_address(&mut invoke_context, &[b"", &[1]], &address),
3911 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
3912 );
3913 }
3914
3915 #[test]
3916 fn test_find_program_address() {
3917 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3918 let cost = invoke_context
3919 .get_compute_budget()
3920 .create_program_address_units;
3921 let address = bpf_loader_upgradeable::id();
3922 let max_tries = 256; for _ in 0..1_000 {
3925 let address = Pubkey::new_unique();
3926 invoke_context.mock_set_remaining(cost * max_tries);
3927 let (found_address, bump_seed) =
3928 try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address)
3929 .unwrap();
3930 assert_eq!(
3931 found_address,
3932 create_program_address(
3933 &mut invoke_context,
3934 &[b"Lil'", b"Bits", &[bump_seed]],
3935 &address,
3936 )
3937 .unwrap()
3938 );
3939 }
3940
3941 let seeds: &[&[u8]] = &[b""];
3942 invoke_context.mock_set_remaining(cost * max_tries);
3943 let (_, bump_seed) =
3944 try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
3945 invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64));
3946 try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
3947 invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
3948 assert_matches!(
3949 try_find_program_address(&mut invoke_context, seeds, &address),
3950 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
3951 );
3952
3953 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
3954 invoke_context.mock_set_remaining(cost * (max_tries - 1));
3955 assert_matches!(
3956 try_find_program_address(&mut invoke_context, &[exceeded_seed], &address),
3957 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
3958 );
3959 let exceeded_seeds: &[&[u8]] = &[
3960 &[1],
3961 &[2],
3962 &[3],
3963 &[4],
3964 &[5],
3965 &[6],
3966 &[7],
3967 &[8],
3968 &[9],
3969 &[10],
3970 &[11],
3971 &[12],
3972 &[13],
3973 &[14],
3974 &[15],
3975 &[16],
3976 &[17],
3977 ];
3978 invoke_context.mock_set_remaining(cost * (max_tries - 1));
3979 assert_matches!(
3980 try_find_program_address(&mut invoke_context, exceeded_seeds, &address),
3981 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
3982 );
3983
3984 assert_matches!(
3985 call_program_address_common(
3986 &mut invoke_context,
3987 seeds,
3988 &address,
3989 true,
3990 SyscallTryFindProgramAddress::rust,
3991 ),
3992 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
3993 );
3994 }
3995
3996 #[test]
3997 fn test_syscall_big_mod_exp() {
3998 let config = Config::default();
3999 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4000
4001 const VADDR_PARAMS: u64 = 0x100000000;
4002 const MAX_LEN: u64 = 512;
4003 const INV_LEN: u64 = MAX_LEN + 1;
4004 let data: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4005 const VADDR_DATA: u64 = 0x200000000;
4006
4007 let mut data_out: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4008 const VADDR_OUT: u64 = 0x300000000;
4009
4010 {
4012 let params_max_len = BigModExpParams {
4013 base: VADDR_DATA as *const u8,
4014 base_len: MAX_LEN,
4015 exponent: VADDR_DATA as *const u8,
4016 exponent_len: MAX_LEN,
4017 modulus: VADDR_DATA as *const u8,
4018 modulus_len: MAX_LEN,
4019 };
4020
4021 let mut memory_mapping = MemoryMapping::new(
4022 vec![
4023 MemoryRegion::new_readonly(bytes_of(¶ms_max_len), VADDR_PARAMS),
4024 MemoryRegion::new_readonly(&data, VADDR_DATA),
4025 MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4026 ],
4027 &config,
4028 &SBPFVersion::V2,
4029 )
4030 .unwrap();
4031
4032 let budget = invoke_context.get_compute_budget();
4033 invoke_context.mock_set_remaining(
4034 budget.syscall_base_cost
4035 + (MAX_LEN * MAX_LEN) / budget.big_modular_exponentiation_cost,
4036 );
4037
4038 let result = SyscallBigModExp::rust(
4039 &mut invoke_context,
4040 VADDR_PARAMS,
4041 VADDR_OUT,
4042 0,
4043 0,
4044 0,
4045 &mut memory_mapping,
4046 );
4047
4048 assert_eq!(result.unwrap(), 0);
4049 }
4050
4051 {
4053 let params_inv_len = BigModExpParams {
4054 base: VADDR_DATA as *const u8,
4055 base_len: INV_LEN,
4056 exponent: VADDR_DATA as *const u8,
4057 exponent_len: INV_LEN,
4058 modulus: VADDR_DATA as *const u8,
4059 modulus_len: INV_LEN,
4060 };
4061
4062 let mut memory_mapping = MemoryMapping::new(
4063 vec![
4064 MemoryRegion::new_readonly(bytes_of(¶ms_inv_len), VADDR_PARAMS),
4065 MemoryRegion::new_readonly(&data, VADDR_DATA),
4066 MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4067 ],
4068 &config,
4069 &SBPFVersion::V2,
4070 )
4071 .unwrap();
4072
4073 let budget = invoke_context.get_compute_budget();
4074 invoke_context.mock_set_remaining(
4075 budget.syscall_base_cost
4076 + (INV_LEN * INV_LEN) / budget.big_modular_exponentiation_cost,
4077 );
4078
4079 let result = SyscallBigModExp::rust(
4080 &mut invoke_context,
4081 VADDR_PARAMS,
4082 VADDR_OUT,
4083 0,
4084 0,
4085 0,
4086 &mut memory_mapping,
4087 );
4088
4089 assert_matches!(
4090 result,
4091 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::InvalidLength
4092 );
4093 }
4094 }
4095
4096 #[test]
4097 fn test_check_type_assumptions() {
4098 check_type_assumptions();
4099 }
4100
4101 fn bytes_of<T>(val: &T) -> &[u8] {
4102 let size = mem::size_of::<T>();
4103 unsafe { slice::from_raw_parts(std::slice::from_ref(val).as_ptr().cast(), size) }
4104 }
4105
4106 fn bytes_of_mut<T>(val: &mut T) -> &mut [u8] {
4107 let size = mem::size_of::<T>();
4108 unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) }
4109 }
4110
4111 pub fn bytes_of_slice<T>(val: &[T]) -> &[u8] {
4112 let size = val.len().wrapping_mul(mem::size_of::<T>());
4113 unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) }
4114 }
4115
4116 pub fn bytes_of_slice_mut<T>(val: &mut [T]) -> &mut [u8] {
4117 let size = val.len().wrapping_mul(mem::size_of::<T>());
4118 unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) }
4119 }
4120
4121 #[test]
4122 fn test_address_is_aligned() {
4123 for address in 0..std::mem::size_of::<u64>() {
4124 assert_eq!(address_is_aligned::<u64>(address as u64), address == 0);
4125 }
4126 }
4127}