1#![allow(deprecated)]
2
3pub use self::{
4 cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust},
5 logging::{
6 SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64,
7 },
8 mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
9 sysvar::{
10 SyscallGetClockSysvar, SyscallGetEpochRewardsSysvar, SyscallGetEpochScheduleSysvar,
11 SyscallGetFeesSysvar, SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
12 SyscallGetSysvar,
13 },
14};
15#[allow(deprecated)]
16use {
17 crate::mem_ops::is_nonoverlapping,
18 atlas_account_info::AccountInfo,
19 atlas_big_mod_exp::{big_mod_exp, BigModExpParams},
20 atlas_blake3_hasher as blake3,
21 atlas_bn254::prelude::{
22 alt_bn128_addition, alt_bn128_multiplication, alt_bn128_multiplication_128,
23 alt_bn128_pairing, AltBn128Error, ALT_BN128_ADDITION_OUTPUT_LEN,
24 ALT_BN128_MULTIPLICATION_OUTPUT_LEN, ALT_BN128_PAIRING_ELEMENT_LEN,
25 ALT_BN128_PAIRING_OUTPUT_LEN,
26 },
27 atlas_cpi::MAX_RETURN_DATA,
28 atlas_hash::Hash,
29 atlas_instruction::{error::InstructionError, AccountMeta, ProcessedSiblingInstruction},
30 atlas_keccak_hasher as keccak, atlas_poseidon as poseidon,
31 atlas_program_entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS},
32 atlas_program_runtime::{
33 execution_budget::{SVMTransactionExecutionBudget, SVMTransactionExecutionCost},
34 invoke_context::InvokeContext,
35 stable_log,
36 },
37 atlas_pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN, PUBKEY_BYTES},
38 atlas_sbpf::{
39 declare_builtin_function,
40 memory_region::{AccessType, MemoryMapping},
41 program::{BuiltinProgram, SBPFVersion},
42 vm::Config,
43 },
44 atlas_sdk_ids::{bpf_loader, bpf_loader_deprecated, native_loader},
45 atlas_secp256k1_recover::{
46 Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
47 },
48 atlas_sha256_hasher::Hasher,
49 atlas_svm_feature_set::SVMFeatureSet,
50 atlas_svm_log_collector::{ic_logger_msg, ic_msg},
51 atlas_svm_timings::ExecuteTimings,
52 atlas_svm_type_overrides::sync::Arc,
53 atlas_sysvar::SysvarSerialize,
54 atlas_transaction_context::IndexOfAccount,
55 std::{
56 alloc::Layout,
57 marker::PhantomData,
58 mem::{align_of, size_of},
59 slice::from_raw_parts_mut,
60 str::{from_utf8, Utf8Error},
61 },
62 thiserror::Error as ThisError,
63};
64
65mod cpi;
66mod logging;
67mod mem_ops;
68mod sysvar;
69
70const MAX_SIGNERS: usize = 16;
72
73#[derive(Debug, ThisError, PartialEq, Eq)]
75pub enum SyscallError {
76 #[error("{0}: {1:?}")]
77 InvalidString(Utf8Error, Vec<u8>),
78 #[error("SBF program panicked")]
79 Abort,
80 #[error("SBF program Panicked in {0} at {1}:{2}")]
81 Panic(String, u64, u64),
82 #[error("Cannot borrow invoke context")]
83 InvokeContextBorrowFailed,
84 #[error("Malformed signer seed: {0}: {1:?}")]
85 MalformedSignerSeed(Utf8Error, Vec<u8>),
86 #[error("Could not create program address with signer seeds: {0}")]
87 BadSeeds(PubkeyError),
88 #[error("Program {0} not supported by inner instructions")]
89 ProgramNotSupported(Pubkey),
90 #[error("Unaligned pointer")]
91 UnalignedPointer,
92 #[error("Too many signers")]
93 TooManySigners,
94 #[error("Instruction passed to inner instruction is too large ({0} > {1})")]
95 InstructionTooLarge(usize, usize),
96 #[error("Too many accounts passed to inner instruction")]
97 TooManyAccounts,
98 #[error("Overlapping copy")]
99 CopyOverlapping,
100 #[error("Return data too large ({0} > {1})")]
101 ReturnDataTooLarge(u64, u64),
102 #[error("Hashing too many sequences")]
103 TooManySlices,
104 #[error("InvalidLength")]
105 InvalidLength,
106 #[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")]
107 MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 },
108 #[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")]
109 MaxInstructionAccountsExceeded {
110 num_accounts: u64,
111 max_accounts: u64,
112 },
113 #[error(
114 "Invoked an instruction with too many account info's ({num_account_infos} > \
115 {max_account_infos})"
116 )]
117 MaxInstructionAccountInfosExceeded {
118 num_account_infos: u64,
119 max_account_infos: u64,
120 },
121 #[error("InvalidAttribute")]
122 InvalidAttribute,
123 #[error("Invalid pointer")]
124 InvalidPointer,
125 #[error("Arithmetic overflow")]
126 ArithmeticOverflow,
127}
128
129type Error = Box<dyn std::error::Error>;
130
131trait HasherImpl {
132 const NAME: &'static str;
133 type Output: AsRef<[u8]>;
134
135 fn create_hasher() -> Self;
136 fn hash(&mut self, val: &[u8]);
137 fn result(self) -> Self::Output;
138 fn get_base_cost(compute_cost: &SVMTransactionExecutionCost) -> u64;
139 fn get_byte_cost(compute_cost: &SVMTransactionExecutionCost) -> u64;
140 fn get_max_slices(compute_budget: &SVMTransactionExecutionBudget) -> u64;
141}
142
143struct Sha256Hasher(Hasher);
144struct Blake3Hasher(blake3::Hasher);
145struct Keccak256Hasher(keccak::Hasher);
146
147impl HasherImpl for Sha256Hasher {
148 const NAME: &'static str = "Sha256";
149 type Output = Hash;
150
151 fn create_hasher() -> Self {
152 Sha256Hasher(Hasher::default())
153 }
154
155 fn hash(&mut self, val: &[u8]) {
156 self.0.hash(val);
157 }
158
159 fn result(self) -> Self::Output {
160 self.0.result()
161 }
162
163 fn get_base_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
164 compute_cost.sha256_base_cost
165 }
166 fn get_byte_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
167 compute_cost.sha256_byte_cost
168 }
169 fn get_max_slices(compute_budget: &SVMTransactionExecutionBudget) -> u64 {
170 compute_budget.sha256_max_slices
171 }
172}
173
174impl HasherImpl for Blake3Hasher {
175 const NAME: &'static str = "Blake3";
176 type Output = blake3::Hash;
177
178 fn create_hasher() -> Self {
179 Blake3Hasher(blake3::Hasher::default())
180 }
181
182 fn hash(&mut self, val: &[u8]) {
183 self.0.hash(val);
184 }
185
186 fn result(self) -> Self::Output {
187 self.0.result()
188 }
189
190 fn get_base_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
191 compute_cost.sha256_base_cost
192 }
193 fn get_byte_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
194 compute_cost.sha256_byte_cost
195 }
196 fn get_max_slices(compute_budget: &SVMTransactionExecutionBudget) -> u64 {
197 compute_budget.sha256_max_slices
198 }
199}
200
201impl HasherImpl for Keccak256Hasher {
202 const NAME: &'static str = "Keccak256";
203 type Output = keccak::Hash;
204
205 fn create_hasher() -> Self {
206 Keccak256Hasher(keccak::Hasher::default())
207 }
208
209 fn hash(&mut self, val: &[u8]) {
210 self.0.hash(val);
211 }
212
213 fn result(self) -> Self::Output {
214 self.0.result()
215 }
216
217 fn get_base_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
218 compute_cost.sha256_base_cost
219 }
220 fn get_byte_cost(compute_cost: &SVMTransactionExecutionCost) -> u64 {
221 compute_cost.sha256_byte_cost
222 }
223 fn get_max_slices(compute_budget: &SVMTransactionExecutionBudget) -> u64 {
224 compute_budget.sha256_max_slices
225 }
226}
227
228#[repr(C)]
240pub struct VmSlice<T> {
241 ptr: u64,
242 len: u64,
243 resource_type: PhantomData<T>,
244}
245
246impl<T> VmSlice<T> {
247 pub fn new(ptr: u64, len: u64) -> Self {
248 VmSlice {
249 ptr,
250 len,
251 resource_type: PhantomData,
252 }
253 }
254
255 pub fn ptr(&self) -> u64 {
256 self.ptr
257 }
258 pub fn len(&self) -> u64 {
259 self.len
260 }
261
262 pub fn is_empty(&self) -> bool {
263 self.len == 0
264 }
265
266 pub fn resize(&mut self, len: u64) {
269 self.len = len;
270 }
271
272 pub fn translate<'a>(
274 &self,
275 memory_mapping: &'a MemoryMapping,
276 check_aligned: bool,
277 ) -> Result<&'a [T], Error> {
278 translate_slice::<T>(memory_mapping, self.ptr, self.len, check_aligned)
279 }
280}
281
282fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), Error> {
283 invoke_context.consume_checked(amount)?;
284 Ok(())
285}
286
287macro_rules! register_feature_gated_function {
288 ($result:expr, $is_feature_active:expr, $name:expr, $call:expr $(,)?) => {
289 if $is_feature_active {
290 $result.register_function($name, $call)
291 } else {
292 Ok(())
293 }
294 };
295}
296
297pub fn create_program_runtime_environment_v1<'a>(
298 feature_set: &SVMFeatureSet,
299 compute_budget: &SVMTransactionExecutionBudget,
300 reject_deployment_of_broken_elfs: bool,
301 debugging_features: bool,
302) -> Result<BuiltinProgram<InvokeContext<'a>>, Error> {
303 let enable_alt_bn128_syscall = feature_set.enable_alt_bn128_syscall;
304 let enable_alt_bn128_compression_syscall = feature_set.enable_alt_bn128_compression_syscall;
305 let enable_big_mod_exp_syscall = feature_set.enable_big_mod_exp_syscall;
306 let blake3_syscall_enabled = feature_set.blake3_syscall_enabled;
307 let curve25519_syscall_enabled = feature_set.curve25519_syscall_enabled;
308 let disable_fees_sysvar = feature_set.disable_fees_sysvar;
309 let disable_deploy_of_alloc_free_syscall =
310 reject_deployment_of_broken_elfs && feature_set.disable_deploy_of_alloc_free_syscall;
311 let last_restart_slot_syscall_enabled = feature_set.last_restart_slot_sysvar;
312 let enable_poseidon_syscall = feature_set.enable_poseidon_syscall;
313 let remaining_compute_units_syscall_enabled =
314 feature_set.remaining_compute_units_syscall_enabled;
315 let get_sysvar_syscall_enabled = feature_set.get_sysvar_syscall_enabled;
316 let enable_get_epoch_stake_syscall = feature_set.enable_get_epoch_stake_syscall;
317 let min_sbpf_version =
318 if !feature_set.disable_sbpf_v0_execution || feature_set.reenable_sbpf_v0_execution {
319 SBPFVersion::V0
320 } else {
321 SBPFVersion::V3
322 };
323 let max_sbpf_version = if feature_set.enable_sbpf_v3_deployment_and_execution {
324 SBPFVersion::V3
325 } else if feature_set.enable_sbpf_v2_deployment_and_execution {
326 SBPFVersion::V2
327 } else if feature_set.enable_sbpf_v1_deployment_and_execution {
328 SBPFVersion::V1
329 } else {
330 SBPFVersion::V0
331 };
332 debug_assert!(min_sbpf_version <= max_sbpf_version);
333
334 let config = Config {
335 max_call_depth: compute_budget.max_call_depth,
336 stack_frame_size: compute_budget.stack_frame_size,
337 enable_address_translation: true,
338 enable_stack_frame_gaps: true,
339 instruction_meter_checkpoint_distance: 10000,
340 enable_instruction_meter: true,
341 enable_instruction_tracing: debugging_features,
342 enable_symbol_and_section_labels: debugging_features,
343 reject_broken_elfs: reject_deployment_of_broken_elfs,
344 noop_instruction_rate: 256,
345 sanitize_user_provided_values: true,
346 enabled_sbpf_versions: min_sbpf_version..=max_sbpf_version,
347 optimize_rodata: false,
348 aligned_memory_mapping: !feature_set.stricter_abi_and_runtime_constraints,
349 };
351 let mut result = BuiltinProgram::new_loader(config);
352
353 result.register_function("abort", SyscallAbort::vm)?;
355
356 result.register_function("sol_panic_", SyscallPanic::vm)?;
358
359 result.register_function("sol_log_", SyscallLog::vm)?;
361 result.register_function("sol_log_64_", SyscallLogU64::vm)?;
362 result.register_function("sol_log_pubkey", SyscallLogPubkey::vm)?;
363 result.register_function("sol_log_compute_units_", SyscallLogBpfComputeUnits::vm)?;
364
365 result.register_function(
367 "sol_create_program_address",
368 SyscallCreateProgramAddress::vm,
369 )?;
370 result.register_function(
371 "sol_try_find_program_address",
372 SyscallTryFindProgramAddress::vm,
373 )?;
374
375 result.register_function("sol_sha256", SyscallHash::vm::<Sha256Hasher>)?;
377
378 result.register_function("sol_keccak256", SyscallHash::vm::<Keccak256Hasher>)?;
380
381 result.register_function("sol_secp256k1_recover", SyscallSecp256k1Recover::vm)?;
383
384 register_feature_gated_function!(
386 result,
387 blake3_syscall_enabled,
388 "sol_blake3",
389 SyscallHash::vm::<Blake3Hasher>,
390 )?;
391
392 register_feature_gated_function!(
394 result,
395 curve25519_syscall_enabled,
396 "sol_curve_validate_point",
397 SyscallCurvePointValidation::vm,
398 )?;
399 register_feature_gated_function!(
400 result,
401 curve25519_syscall_enabled,
402 "sol_curve_group_op",
403 SyscallCurveGroupOps::vm,
404 )?;
405 register_feature_gated_function!(
406 result,
407 curve25519_syscall_enabled,
408 "sol_curve_multiscalar_mul",
409 SyscallCurveMultiscalarMultiplication::vm,
410 )?;
411
412 result.register_function("sol_get_clock_sysvar", SyscallGetClockSysvar::vm)?;
414 result.register_function(
415 "sol_get_epoch_schedule_sysvar",
416 SyscallGetEpochScheduleSysvar::vm,
417 )?;
418 register_feature_gated_function!(
419 result,
420 !disable_fees_sysvar,
421 "sol_get_fees_sysvar",
422 SyscallGetFeesSysvar::vm,
423 )?;
424 result.register_function("sol_get_rent_sysvar", SyscallGetRentSysvar::vm)?;
425
426 register_feature_gated_function!(
427 result,
428 last_restart_slot_syscall_enabled,
429 "sol_get_last_restart_slot",
430 SyscallGetLastRestartSlotSysvar::vm,
431 )?;
432
433 result.register_function(
434 "sol_get_epoch_rewards_sysvar",
435 SyscallGetEpochRewardsSysvar::vm,
436 )?;
437
438 result.register_function("sol_memcpy_", SyscallMemcpy::vm)?;
440 result.register_function("sol_memmove_", SyscallMemmove::vm)?;
441 result.register_function("sol_memset_", SyscallMemset::vm)?;
442 result.register_function("sol_memcmp_", SyscallMemcmp::vm)?;
443
444 result.register_function(
446 "sol_get_processed_sibling_instruction",
447 SyscallGetProcessedSiblingInstruction::vm,
448 )?;
449
450 result.register_function("sol_get_stack_height", SyscallGetStackHeight::vm)?;
452
453 result.register_function("sol_set_return_data", SyscallSetReturnData::vm)?;
455 result.register_function("sol_get_return_data", SyscallGetReturnData::vm)?;
456
457 result.register_function("sol_invoke_signed_c", SyscallInvokeSignedC::vm)?;
459 result.register_function("sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)?;
460
461 register_feature_gated_function!(
463 result,
464 !disable_deploy_of_alloc_free_syscall,
465 "sol_alloc_free_",
466 SyscallAllocFree::vm,
467 )?;
468
469 register_feature_gated_function!(
471 result,
472 enable_alt_bn128_syscall,
473 "sol_alt_bn128_group_op",
474 SyscallAltBn128::vm,
475 )?;
476
477 register_feature_gated_function!(
479 result,
480 enable_big_mod_exp_syscall,
481 "sol_big_mod_exp",
482 SyscallBigModExp::vm,
483 )?;
484
485 register_feature_gated_function!(
487 result,
488 enable_poseidon_syscall,
489 "sol_poseidon",
490 SyscallPoseidon::vm,
491 )?;
492
493 register_feature_gated_function!(
495 result,
496 remaining_compute_units_syscall_enabled,
497 "sol_remaining_compute_units",
498 SyscallRemainingComputeUnits::vm
499 )?;
500
501 register_feature_gated_function!(
503 result,
504 enable_alt_bn128_compression_syscall,
505 "sol_alt_bn128_compression",
506 SyscallAltBn128Compression::vm,
507 )?;
508
509 register_feature_gated_function!(
511 result,
512 get_sysvar_syscall_enabled,
513 "sol_get_sysvar",
514 SyscallGetSysvar::vm,
515 )?;
516
517 register_feature_gated_function!(
519 result,
520 enable_get_epoch_stake_syscall,
521 "sol_get_epoch_stake",
522 SyscallGetEpochStake::vm,
523 )?;
524
525 result.register_function("sol_log_data", SyscallLogData::vm)?;
527
528 Ok(result)
529}
530
531pub fn create_program_runtime_environment_v2<'a>(
532 compute_budget: &SVMTransactionExecutionBudget,
533 debugging_features: bool,
534) -> BuiltinProgram<InvokeContext<'a>> {
535 let config = Config {
536 max_call_depth: compute_budget.max_call_depth,
537 stack_frame_size: compute_budget.stack_frame_size,
538 enable_address_translation: true, enable_stack_frame_gaps: false,
540 instruction_meter_checkpoint_distance: 10000,
541 enable_instruction_meter: true,
542 enable_instruction_tracing: debugging_features,
543 enable_symbol_and_section_labels: debugging_features,
544 reject_broken_elfs: true,
545 noop_instruction_rate: 256,
546 sanitize_user_provided_values: true,
547 enabled_sbpf_versions: SBPFVersion::Reserved..=SBPFVersion::Reserved,
548 optimize_rodata: true,
549 aligned_memory_mapping: true,
550 };
552 BuiltinProgram::new_loader(config)
553}
554
555fn address_is_aligned<T>(address: u64) -> bool {
556 (address as *mut T as usize)
557 .checked_rem(align_of::<T>())
558 .map(|rem| rem == 0)
559 .expect("T to be non-zero aligned")
560}
561
562#[macro_export]
564macro_rules! translate_inner {
565 ($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
566 Result::<u64, Error>::from(
567 $memory_mapping
568 .$map($access_type, $vm_addr, $len)
569 .map_err(|err| err.into()),
570 )
571 };
572}
573#[macro_export]
575macro_rules! translate_type_inner {
576 ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
577 let host_addr = translate_inner!(
578 $memory_mapping,
579 map,
580 $access_type,
581 $vm_addr,
582 size_of::<$T>() as u64
583 )?;
584 if !$check_aligned {
585 Ok(unsafe { std::mem::transmute::<u64, &mut $T>(host_addr) })
586 } else if !address_is_aligned::<$T>(host_addr) {
587 Err(SyscallError::UnalignedPointer.into())
588 } else {
589 Ok(unsafe { &mut *(host_addr as *mut $T) })
590 }
591 }};
592}
593#[macro_export]
595macro_rules! translate_slice_inner {
596 ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
597 if $len == 0 {
598 return Ok(&mut []);
599 }
600 let total_size = $len.saturating_mul(size_of::<$T>() as u64);
601 if isize::try_from(total_size).is_err() {
602 return Err(SyscallError::InvalidLength.into());
603 }
604 let host_addr = translate_inner!($memory_mapping, map, $access_type, $vm_addr, total_size)?;
605 if $check_aligned && !address_is_aligned::<$T>(host_addr) {
606 return Err(SyscallError::UnalignedPointer.into());
607 }
608 Ok(unsafe { from_raw_parts_mut(host_addr as *mut $T, $len as usize) })
609 }};
610}
611
612fn translate_type<'a, T>(
613 memory_mapping: &'a MemoryMapping,
614 vm_addr: u64,
615 check_aligned: bool,
616) -> Result<&'a T, Error> {
617 translate_type_inner!(memory_mapping, AccessType::Load, vm_addr, T, check_aligned)
618 .map(|value| &*value)
619}
620fn translate_slice<'a, T>(
621 memory_mapping: &'a MemoryMapping,
622 vm_addr: u64,
623 len: u64,
624 check_aligned: bool,
625) -> Result<&'a [T], Error> {
626 translate_slice_inner!(
627 memory_mapping,
628 AccessType::Load,
629 vm_addr,
630 len,
631 T,
632 check_aligned,
633 )
634 .map(|value| &*value)
635}
636
637fn translate_string_and_do(
640 memory_mapping: &MemoryMapping,
641 addr: u64,
642 len: u64,
643 check_aligned: bool,
644 work: &mut dyn FnMut(&str) -> Result<u64, Error>,
645) -> Result<u64, Error> {
646 let buf = translate_slice::<u8>(memory_mapping, addr, len, check_aligned)?;
647 match from_utf8(buf) {
648 Ok(message) => work(message),
649 Err(err) => Err(SyscallError::InvalidString(err, buf.to_vec()).into()),
650 }
651}
652
653fn translate_type_mut<'a, T>(
655 memory_mapping: &'a MemoryMapping,
656 vm_addr: u64,
657 check_aligned: bool,
658) -> Result<&'a mut T, Error> {
659 translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
660}
661fn translate_slice_mut<'a, T>(
663 memory_mapping: &'a MemoryMapping,
664 vm_addr: u64,
665 len: u64,
666 check_aligned: bool,
667) -> Result<&'a mut [T], Error> {
668 translate_slice_inner!(
669 memory_mapping,
670 AccessType::Store,
671 vm_addr,
672 len,
673 T,
674 check_aligned,
675 )
676}
677
678fn touch_type_mut<T>(memory_mapping: &mut MemoryMapping, vm_addr: u64) -> Result<(), Error> {
679 translate_inner!(
680 memory_mapping,
681 map_with_access_violation_handler,
682 AccessType::Store,
683 vm_addr,
684 size_of::<T>() as u64,
685 )
686 .map(|_| ())
687}
688fn touch_slice_mut<T>(
689 memory_mapping: &mut MemoryMapping,
690 vm_addr: u64,
691 element_count: u64,
692) -> Result<(), Error> {
693 if element_count == 0 {
694 return Ok(());
695 }
696 translate_inner!(
697 memory_mapping,
698 map_with_access_violation_handler,
699 AccessType::Store,
700 vm_addr,
701 element_count.saturating_mul(size_of::<T>() as u64),
702 )
703 .map(|_| ())
704}
705
706#[macro_export]
710macro_rules! translate_mut {
711 (internal, $memory_mapping:expr, &mut [$T:ty], $vm_addr_and_element_count:expr) => {
712 touch_slice_mut::<$T>(
713 $memory_mapping,
714 $vm_addr_and_element_count.0,
715 $vm_addr_and_element_count.1,
716 )?
717 };
718 (internal, $memory_mapping:expr, &mut $T:ty, $vm_addr:expr) => {
719 touch_type_mut::<$T>(
720 $memory_mapping,
721 $vm_addr,
722 )?
723 };
724 (internal, $memory_mapping:expr, $check_aligned:expr, &mut [$T:ty], $vm_addr_and_element_count:expr) => {{
725 let slice = translate_slice_mut::<$T>(
726 $memory_mapping,
727 $vm_addr_and_element_count.0,
728 $vm_addr_and_element_count.1,
729 $check_aligned,
730 )?;
731 let host_addr = slice.as_ptr() as usize;
732 (slice, host_addr, std::mem::size_of::<$T>().saturating_mul($vm_addr_and_element_count.1 as usize))
733 }};
734 (internal, $memory_mapping:expr, $check_aligned:expr, &mut $T:ty, $vm_addr:expr) => {{
735 let reference = translate_type_mut::<$T>(
736 $memory_mapping,
737 $vm_addr,
738 $check_aligned,
739 )?;
740 let host_addr = reference as *const _ as usize;
741 (reference, host_addr, std::mem::size_of::<$T>())
742 }};
743 ($memory_mapping:expr, $check_aligned:expr, $(let $binding:ident : &mut $T:tt = map($vm_addr:expr $(, $element_count:expr)?) $try:tt;)+) => {
744 $(let $binding = ($vm_addr $(, $element_count)?);)+
746 $(translate_mut!(internal, $memory_mapping, &mut $T, $binding);)+
748 $(let $binding = translate_mut!(internal, $memory_mapping, $check_aligned, &mut $T, $binding);)+
749 let host_ranges = [
750 $(($binding.1, $binding.2),)+
751 ];
752 for (index, range_a) in host_ranges.get(..host_ranges.len().saturating_sub(1)).unwrap().iter().enumerate() {
753 for range_b in host_ranges.get(index.saturating_add(1)..).unwrap().iter() {
754 if !is_nonoverlapping(range_a.0, range_a.1, range_b.0, range_b.1) {
755 return Err(SyscallError::CopyOverlapping.into());
756 }
757 }
758 }
759 $(let $binding = $binding.0;)+
760 };
761}
762
763declare_builtin_function!(
764 SyscallAbort,
769 fn rust(
770 _invoke_context: &mut InvokeContext,
771 _arg1: u64,
772 _arg2: u64,
773 _arg3: u64,
774 _arg4: u64,
775 _arg5: u64,
776 _memory_mapping: &mut MemoryMapping,
777 ) -> Result<u64, Error> {
778 Err(SyscallError::Abort.into())
779 }
780);
781
782declare_builtin_function!(
783 SyscallPanic,
786 fn rust(
787 invoke_context: &mut InvokeContext,
788 file: u64,
789 len: u64,
790 line: u64,
791 column: u64,
792 _arg5: u64,
793 memory_mapping: &mut MemoryMapping,
794 ) -> Result<u64, Error> {
795 consume_compute_meter(invoke_context, len)?;
796
797 translate_string_and_do(
798 memory_mapping,
799 file,
800 len,
801 invoke_context.get_check_aligned(),
802 &mut |string: &str| Err(SyscallError::Panic(string.to_string(), line, column).into()),
803 )
804 }
805);
806
807declare_builtin_function!(
808 SyscallAllocFree,
815 fn rust(
816 invoke_context: &mut InvokeContext,
817 size: u64,
818 free_addr: u64,
819 _arg3: u64,
820 _arg4: u64,
821 _arg5: u64,
822 _memory_mapping: &mut MemoryMapping,
823 ) -> Result<u64, Error> {
824 let align = if invoke_context.get_check_aligned() {
825 BPF_ALIGN_OF_U128
826 } else {
827 align_of::<u8>()
828 };
829 let Ok(layout) = Layout::from_size_align(size as usize, align) else {
830 return Ok(0);
831 };
832 let allocator = &mut invoke_context.get_syscall_context_mut()?.allocator;
833 if free_addr == 0 {
834 match allocator.alloc(layout) {
835 Ok(addr) => Ok(addr),
836 Err(_) => Ok(0),
837 }
838 } else {
839 Ok(0)
841 }
842 }
843);
844
845fn translate_and_check_program_address_inputs<'a>(
846 seeds_addr: u64,
847 seeds_len: u64,
848 program_id_addr: u64,
849 memory_mapping: &'a mut MemoryMapping,
850 check_aligned: bool,
851) -> Result<(Vec<&'a [u8]>, &'a Pubkey), Error> {
852 let untranslated_seeds =
853 translate_slice::<VmSlice<u8>>(memory_mapping, seeds_addr, seeds_len, check_aligned)?;
854 if untranslated_seeds.len() > MAX_SEEDS {
855 return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
856 }
857 let seeds = untranslated_seeds
858 .iter()
859 .map(|untranslated_seed| {
860 if untranslated_seed.len() > MAX_SEED_LEN as u64 {
861 return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
862 }
863 untranslated_seed.translate(memory_mapping, check_aligned)
864 })
865 .collect::<Result<Vec<_>, Error>>()?;
866 let program_id = translate_type::<Pubkey>(memory_mapping, program_id_addr, check_aligned)?;
867 Ok((seeds, program_id))
868}
869
870declare_builtin_function!(
871 SyscallCreateProgramAddress,
873 fn rust(
874 invoke_context: &mut InvokeContext,
875 seeds_addr: u64,
876 seeds_len: u64,
877 program_id_addr: u64,
878 address_addr: u64,
879 _arg5: u64,
880 memory_mapping: &mut MemoryMapping,
881 ) -> Result<u64, Error> {
882 let cost = invoke_context
883 .get_execution_cost()
884 .create_program_address_units;
885 consume_compute_meter(invoke_context, cost)?;
886
887 let (seeds, program_id) = translate_and_check_program_address_inputs(
888 seeds_addr,
889 seeds_len,
890 program_id_addr,
891 memory_mapping,
892 invoke_context.get_check_aligned(),
893 )?;
894
895 let Ok(new_address) = Pubkey::create_program_address(&seeds, program_id) else {
896 return Ok(1);
897 };
898 translate_mut!(
899 memory_mapping,
900 invoke_context.get_check_aligned(),
901 let address: &mut [u8] = map(address_addr, std::mem::size_of::<Pubkey>() as u64)?;
902 );
903 address.copy_from_slice(new_address.as_ref());
904 Ok(0)
905 }
906);
907
908declare_builtin_function!(
909 SyscallTryFindProgramAddress,
911 fn rust(
912 invoke_context: &mut InvokeContext,
913 seeds_addr: u64,
914 seeds_len: u64,
915 program_id_addr: u64,
916 address_addr: u64,
917 bump_seed_addr: u64,
918 memory_mapping: &mut MemoryMapping,
919 ) -> Result<u64, Error> {
920 let cost = invoke_context
921 .get_execution_cost()
922 .create_program_address_units;
923 consume_compute_meter(invoke_context, cost)?;
924
925 let (seeds, program_id) = translate_and_check_program_address_inputs(
926 seeds_addr,
927 seeds_len,
928 program_id_addr,
929 memory_mapping,
930 invoke_context.get_check_aligned(),
931 )?;
932
933 let mut bump_seed = [u8::MAX];
934 for _ in 0..u8::MAX {
935 {
936 let mut seeds_with_bump = seeds.to_vec();
937 seeds_with_bump.push(&bump_seed);
938
939 if let Ok(new_address) =
940 Pubkey::create_program_address(&seeds_with_bump, program_id)
941 {
942 translate_mut!(
943 memory_mapping,
944 invoke_context.get_check_aligned(),
945 let bump_seed_ref: &mut u8 = map(bump_seed_addr)?;
946 let address: &mut [u8] = map(address_addr, std::mem::size_of::<Pubkey>() as u64)?;
947 );
948 *bump_seed_ref = bump_seed[0];
949 address.copy_from_slice(new_address.as_ref());
950 return Ok(0);
951 }
952 }
953 bump_seed[0] = bump_seed[0].saturating_sub(1);
954 consume_compute_meter(invoke_context, cost)?;
955 }
956 Ok(1)
957 }
958);
959
960declare_builtin_function!(
961 SyscallSecp256k1Recover,
963 fn rust(
964 invoke_context: &mut InvokeContext,
965 hash_addr: u64,
966 recovery_id_val: u64,
967 signature_addr: u64,
968 result_addr: u64,
969 _arg5: u64,
970 memory_mapping: &mut MemoryMapping,
971 ) -> Result<u64, Error> {
972 let cost = invoke_context.get_execution_cost().secp256k1_recover_cost;
973 consume_compute_meter(invoke_context, cost)?;
974
975 translate_mut!(
976 memory_mapping,
977 invoke_context.get_check_aligned(),
978 let secp256k1_recover_result: &mut [u8] = map(result_addr, SECP256K1_PUBLIC_KEY_LENGTH as u64)?;
979 );
980 let hash = translate_slice::<u8>(
981 memory_mapping,
982 hash_addr,
983 keccak::HASH_BYTES as u64,
984 invoke_context.get_check_aligned(),
985 )?;
986 let signature = translate_slice::<u8>(
987 memory_mapping,
988 signature_addr,
989 SECP256K1_SIGNATURE_LENGTH as u64,
990 invoke_context.get_check_aligned(),
991 )?;
992
993 let Ok(message) = libsecp256k1::Message::parse_slice(hash) else {
994 return Ok(Secp256k1RecoverError::InvalidHash.into());
995 };
996 let Ok(adjusted_recover_id_val) = recovery_id_val.try_into() else {
997 return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
998 };
999 let Ok(recovery_id) = libsecp256k1::RecoveryId::parse(adjusted_recover_id_val) else {
1000 return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
1001 };
1002 let Ok(signature) = libsecp256k1::Signature::parse_standard_slice(signature) else {
1003 return Ok(Secp256k1RecoverError::InvalidSignature.into());
1004 };
1005
1006 let public_key = match libsecp256k1::recover(&message, &signature, &recovery_id) {
1007 Ok(key) => key.serialize(),
1008 Err(_) => {
1009 return Ok(Secp256k1RecoverError::InvalidSignature.into());
1010 }
1011 };
1012
1013 secp256k1_recover_result.copy_from_slice(&public_key[1..65]);
1014 Ok(SUCCESS)
1015 }
1016);
1017
1018declare_builtin_function!(
1019 SyscallCurvePointValidation,
1023 fn rust(
1024 invoke_context: &mut InvokeContext,
1025 curve_id: u64,
1026 point_addr: u64,
1027 _arg3: u64,
1028 _arg4: u64,
1029 _arg5: u64,
1030 memory_mapping: &mut MemoryMapping,
1031 ) -> Result<u64, Error> {
1032 use atlas_curve25519::{curve_syscall_traits::*, edwards, ristretto};
1033 match curve_id {
1034 CURVE25519_EDWARDS => {
1035 let cost = invoke_context
1036 .get_execution_cost()
1037 .curve25519_edwards_validate_point_cost;
1038 consume_compute_meter(invoke_context, cost)?;
1039
1040 let point = translate_type::<edwards::PodEdwardsPoint>(
1041 memory_mapping,
1042 point_addr,
1043 invoke_context.get_check_aligned(),
1044 )?;
1045
1046 if edwards::validate_edwards(point) {
1047 Ok(0)
1048 } else {
1049 Ok(1)
1050 }
1051 }
1052 CURVE25519_RISTRETTO => {
1053 let cost = invoke_context
1054 .get_execution_cost()
1055 .curve25519_ristretto_validate_point_cost;
1056 consume_compute_meter(invoke_context, cost)?;
1057
1058 let point = translate_type::<ristretto::PodRistrettoPoint>(
1059 memory_mapping,
1060 point_addr,
1061 invoke_context.get_check_aligned(),
1062 )?;
1063
1064 if ristretto::validate_ristretto(point) {
1065 Ok(0)
1066 } else {
1067 Ok(1)
1068 }
1069 }
1070 _ => {
1071 if invoke_context.get_feature_set().abort_on_invalid_curve {
1072 Err(SyscallError::InvalidAttribute.into())
1073 } else {
1074 Ok(1)
1075 }
1076 }
1077 }
1078 }
1079);
1080
1081declare_builtin_function!(
1082 SyscallCurveGroupOps,
1086 fn rust(
1087 invoke_context: &mut InvokeContext,
1088 curve_id: u64,
1089 group_op: u64,
1090 left_input_addr: u64,
1091 right_input_addr: u64,
1092 result_point_addr: u64,
1093 memory_mapping: &mut MemoryMapping,
1094 ) -> Result<u64, Error> {
1095 use atlas_curve25519::{
1096 curve_syscall_traits::*,
1097 edwards::{self, PodEdwardsPoint},
1098 ristretto::{self, PodRistrettoPoint},
1099 scalar,
1100 };
1101 match curve_id {
1102 CURVE25519_EDWARDS => match group_op {
1103 ADD => {
1104 let cost = invoke_context
1105 .get_execution_cost()
1106 .curve25519_edwards_add_cost;
1107 consume_compute_meter(invoke_context, cost)?;
1108
1109 let left_point = translate_type::<PodEdwardsPoint>(
1110 memory_mapping,
1111 left_input_addr,
1112 invoke_context.get_check_aligned(),
1113 )?;
1114 let right_point = translate_type::<PodEdwardsPoint>(
1115 memory_mapping,
1116 right_input_addr,
1117 invoke_context.get_check_aligned(),
1118 )?;
1119
1120 if let Some(result_point) = edwards::add_edwards(left_point, right_point) {
1121 translate_mut!(
1122 memory_mapping,
1123 invoke_context.get_check_aligned(),
1124 let result_point_ref_mut: &mut PodEdwardsPoint = map(result_point_addr)?;
1125 );
1126 *result_point_ref_mut = result_point;
1127 Ok(0)
1128 } else {
1129 Ok(1)
1130 }
1131 }
1132 SUB => {
1133 let cost = invoke_context
1134 .get_execution_cost()
1135 .curve25519_edwards_subtract_cost;
1136 consume_compute_meter(invoke_context, cost)?;
1137
1138 let left_point = translate_type::<PodEdwardsPoint>(
1139 memory_mapping,
1140 left_input_addr,
1141 invoke_context.get_check_aligned(),
1142 )?;
1143 let right_point = translate_type::<PodEdwardsPoint>(
1144 memory_mapping,
1145 right_input_addr,
1146 invoke_context.get_check_aligned(),
1147 )?;
1148
1149 if let Some(result_point) = edwards::subtract_edwards(left_point, right_point) {
1150 translate_mut!(
1151 memory_mapping,
1152 invoke_context.get_check_aligned(),
1153 let result_point_ref_mut: &mut PodEdwardsPoint = map(result_point_addr)?;
1154 );
1155 *result_point_ref_mut = result_point;
1156 Ok(0)
1157 } else {
1158 Ok(1)
1159 }
1160 }
1161 MUL => {
1162 let cost = invoke_context
1163 .get_execution_cost()
1164 .curve25519_edwards_multiply_cost;
1165 consume_compute_meter(invoke_context, cost)?;
1166
1167 let scalar = translate_type::<scalar::PodScalar>(
1168 memory_mapping,
1169 left_input_addr,
1170 invoke_context.get_check_aligned(),
1171 )?;
1172 let input_point = translate_type::<PodEdwardsPoint>(
1173 memory_mapping,
1174 right_input_addr,
1175 invoke_context.get_check_aligned(),
1176 )?;
1177
1178 if let Some(result_point) = edwards::multiply_edwards(scalar, input_point) {
1179 translate_mut!(
1180 memory_mapping,
1181 invoke_context.get_check_aligned(),
1182 let result_point_ref_mut: &mut PodEdwardsPoint = map(result_point_addr)?;
1183 );
1184 *result_point_ref_mut = result_point;
1185 Ok(0)
1186 } else {
1187 Ok(1)
1188 }
1189 }
1190 _ => {
1191 if invoke_context.get_feature_set().abort_on_invalid_curve {
1192 Err(SyscallError::InvalidAttribute.into())
1193 } else {
1194 Ok(1)
1195 }
1196 }
1197 },
1198
1199 CURVE25519_RISTRETTO => match group_op {
1200 ADD => {
1201 let cost = invoke_context
1202 .get_execution_cost()
1203 .curve25519_ristretto_add_cost;
1204 consume_compute_meter(invoke_context, cost)?;
1205
1206 let left_point = translate_type::<PodRistrettoPoint>(
1207 memory_mapping,
1208 left_input_addr,
1209 invoke_context.get_check_aligned(),
1210 )?;
1211 let right_point = translate_type::<PodRistrettoPoint>(
1212 memory_mapping,
1213 right_input_addr,
1214 invoke_context.get_check_aligned(),
1215 )?;
1216
1217 if let Some(result_point) = ristretto::add_ristretto(left_point, right_point) {
1218 translate_mut!(
1219 memory_mapping,
1220 invoke_context.get_check_aligned(),
1221 let result_point_ref_mut: &mut PodRistrettoPoint = map(result_point_addr)?;
1222 );
1223 *result_point_ref_mut = result_point;
1224 Ok(0)
1225 } else {
1226 Ok(1)
1227 }
1228 }
1229 SUB => {
1230 let cost = invoke_context
1231 .get_execution_cost()
1232 .curve25519_ristretto_subtract_cost;
1233 consume_compute_meter(invoke_context, cost)?;
1234
1235 let left_point = translate_type::<PodRistrettoPoint>(
1236 memory_mapping,
1237 left_input_addr,
1238 invoke_context.get_check_aligned(),
1239 )?;
1240 let right_point = translate_type::<PodRistrettoPoint>(
1241 memory_mapping,
1242 right_input_addr,
1243 invoke_context.get_check_aligned(),
1244 )?;
1245
1246 if let Some(result_point) =
1247 ristretto::subtract_ristretto(left_point, right_point)
1248 {
1249 translate_mut!(
1250 memory_mapping,
1251 invoke_context.get_check_aligned(),
1252 let result_point_ref_mut: &mut PodRistrettoPoint = map(result_point_addr)?;
1253 );
1254 *result_point_ref_mut = result_point;
1255 Ok(0)
1256 } else {
1257 Ok(1)
1258 }
1259 }
1260 MUL => {
1261 let cost = invoke_context
1262 .get_execution_cost()
1263 .curve25519_ristretto_multiply_cost;
1264 consume_compute_meter(invoke_context, cost)?;
1265
1266 let scalar = translate_type::<scalar::PodScalar>(
1267 memory_mapping,
1268 left_input_addr,
1269 invoke_context.get_check_aligned(),
1270 )?;
1271 let input_point = translate_type::<PodRistrettoPoint>(
1272 memory_mapping,
1273 right_input_addr,
1274 invoke_context.get_check_aligned(),
1275 )?;
1276
1277 if let Some(result_point) = ristretto::multiply_ristretto(scalar, input_point) {
1278 translate_mut!(
1279 memory_mapping,
1280 invoke_context.get_check_aligned(),
1281 let result_point_ref_mut: &mut PodRistrettoPoint = map(result_point_addr)?;
1282 );
1283 *result_point_ref_mut = result_point;
1284 Ok(0)
1285 } else {
1286 Ok(1)
1287 }
1288 }
1289 _ => {
1290 if invoke_context.get_feature_set().abort_on_invalid_curve {
1291 Err(SyscallError::InvalidAttribute.into())
1292 } else {
1293 Ok(1)
1294 }
1295 }
1296 },
1297
1298 _ => {
1299 if invoke_context.get_feature_set().abort_on_invalid_curve {
1300 Err(SyscallError::InvalidAttribute.into())
1301 } else {
1302 Ok(1)
1303 }
1304 }
1305 }
1306 }
1307);
1308
1309declare_builtin_function!(
1310 SyscallCurveMultiscalarMultiplication,
1314 fn rust(
1315 invoke_context: &mut InvokeContext,
1316 curve_id: u64,
1317 scalars_addr: u64,
1318 points_addr: u64,
1319 points_len: u64,
1320 result_point_addr: u64,
1321 memory_mapping: &mut MemoryMapping,
1322 ) -> Result<u64, Error> {
1323 use atlas_curve25519::{
1324 curve_syscall_traits::*,
1325 edwards::{self, PodEdwardsPoint},
1326 ristretto::{self, PodRistrettoPoint},
1327 scalar,
1328 };
1329
1330 if points_len > 512 {
1331 return Err(Box::new(SyscallError::InvalidLength));
1332 }
1333
1334 match curve_id {
1335 CURVE25519_EDWARDS => {
1336 let cost = invoke_context
1337 .get_execution_cost()
1338 .curve25519_edwards_msm_base_cost
1339 .saturating_add(
1340 invoke_context
1341 .get_execution_cost()
1342 .curve25519_edwards_msm_incremental_cost
1343 .saturating_mul(points_len.saturating_sub(1)),
1344 );
1345 consume_compute_meter(invoke_context, cost)?;
1346
1347 let scalars = translate_slice::<scalar::PodScalar>(
1348 memory_mapping,
1349 scalars_addr,
1350 points_len,
1351 invoke_context.get_check_aligned(),
1352 )?;
1353
1354 let points = translate_slice::<PodEdwardsPoint>(
1355 memory_mapping,
1356 points_addr,
1357 points_len,
1358 invoke_context.get_check_aligned(),
1359 )?;
1360
1361 if let Some(result_point) = edwards::multiscalar_multiply_edwards(scalars, points) {
1362 translate_mut!(
1363 memory_mapping,
1364 invoke_context.get_check_aligned(),
1365 let result_point_ref_mut: &mut PodEdwardsPoint = map(result_point_addr)?;
1366 );
1367 *result_point_ref_mut = result_point;
1368 Ok(0)
1369 } else {
1370 Ok(1)
1371 }
1372 }
1373
1374 CURVE25519_RISTRETTO => {
1375 let cost = invoke_context
1376 .get_execution_cost()
1377 .curve25519_ristretto_msm_base_cost
1378 .saturating_add(
1379 invoke_context
1380 .get_execution_cost()
1381 .curve25519_ristretto_msm_incremental_cost
1382 .saturating_mul(points_len.saturating_sub(1)),
1383 );
1384 consume_compute_meter(invoke_context, cost)?;
1385
1386 let scalars = translate_slice::<scalar::PodScalar>(
1387 memory_mapping,
1388 scalars_addr,
1389 points_len,
1390 invoke_context.get_check_aligned(),
1391 )?;
1392
1393 let points = translate_slice::<PodRistrettoPoint>(
1394 memory_mapping,
1395 points_addr,
1396 points_len,
1397 invoke_context.get_check_aligned(),
1398 )?;
1399
1400 if let Some(result_point) =
1401 ristretto::multiscalar_multiply_ristretto(scalars, points)
1402 {
1403 translate_mut!(
1404 memory_mapping,
1405 invoke_context.get_check_aligned(),
1406 let result_point_ref_mut: &mut PodRistrettoPoint = map(result_point_addr)?;
1407 );
1408 *result_point_ref_mut = result_point;
1409 Ok(0)
1410 } else {
1411 Ok(1)
1412 }
1413 }
1414
1415 _ => {
1416 if invoke_context.get_feature_set().abort_on_invalid_curve {
1417 Err(SyscallError::InvalidAttribute.into())
1418 } else {
1419 Ok(1)
1420 }
1421 }
1422 }
1423 }
1424);
1425
1426declare_builtin_function!(
1427 SyscallSetReturnData,
1429 fn rust(
1430 invoke_context: &mut InvokeContext,
1431 addr: u64,
1432 len: u64,
1433 _arg3: u64,
1434 _arg4: u64,
1435 _arg5: u64,
1436 memory_mapping: &mut MemoryMapping,
1437 ) -> Result<u64, Error> {
1438 let execution_cost = invoke_context.get_execution_cost();
1439
1440 let cost = len
1441 .checked_div(execution_cost.cpi_bytes_per_unit)
1442 .unwrap_or(u64::MAX)
1443 .saturating_add(execution_cost.syscall_base_cost);
1444 consume_compute_meter(invoke_context, cost)?;
1445
1446 if len > MAX_RETURN_DATA as u64 {
1447 return Err(SyscallError::ReturnDataTooLarge(len, MAX_RETURN_DATA as u64).into());
1448 }
1449
1450 let return_data = if len == 0 {
1451 Vec::new()
1452 } else {
1453 translate_slice::<u8>(
1454 memory_mapping,
1455 addr,
1456 len,
1457 invoke_context.get_check_aligned(),
1458 )?
1459 .to_vec()
1460 };
1461 let transaction_context = &mut invoke_context.transaction_context;
1462 let program_id = *transaction_context
1463 .get_current_instruction_context()
1464 .and_then(|instruction_context| {
1465 instruction_context.get_program_key()
1466 })?;
1467
1468 transaction_context.set_return_data(program_id, return_data)?;
1469
1470 Ok(0)
1471 }
1472);
1473
1474declare_builtin_function!(
1475 SyscallGetReturnData,
1477 fn rust(
1478 invoke_context: &mut InvokeContext,
1479 return_data_addr: u64,
1480 length: u64,
1481 program_id_addr: u64,
1482 _arg4: u64,
1483 _arg5: u64,
1484 memory_mapping: &mut MemoryMapping,
1485 ) -> Result<u64, Error> {
1486 let execution_cost = invoke_context.get_execution_cost();
1487
1488 consume_compute_meter(invoke_context, execution_cost.syscall_base_cost)?;
1489
1490 let (program_id, return_data) = invoke_context.transaction_context.get_return_data();
1491 let length = length.min(return_data.len() as u64);
1492 if length != 0 {
1493 let cost = length
1494 .saturating_add(size_of::<Pubkey>() as u64)
1495 .checked_div(execution_cost.cpi_bytes_per_unit)
1496 .unwrap_or(u64::MAX);
1497 consume_compute_meter(invoke_context, cost)?;
1498
1499 translate_mut!(
1500 memory_mapping,
1501 invoke_context.get_check_aligned(),
1502 let return_data_result: &mut [u8] = map(return_data_addr, length)?;
1503 let program_id_result: &mut Pubkey = map(program_id_addr)?;
1504 );
1505
1506 let to_slice = return_data_result;
1507 let from_slice = return_data
1508 .get(..length as usize)
1509 .ok_or(SyscallError::InvokeContextBorrowFailed)?;
1510 if to_slice.len() != from_slice.len() {
1511 return Err(SyscallError::InvalidLength.into());
1512 }
1513 to_slice.copy_from_slice(from_slice);
1514 *program_id_result = *program_id;
1515 }
1516
1517 Ok(return_data.len() as u64)
1519 }
1520);
1521
1522declare_builtin_function!(
1523 SyscallGetProcessedSiblingInstruction,
1525 fn rust(
1526 invoke_context: &mut InvokeContext,
1527 index: u64,
1528 meta_addr: u64,
1529 program_id_addr: u64,
1530 data_addr: u64,
1531 accounts_addr: u64,
1532 memory_mapping: &mut MemoryMapping,
1533 ) -> Result<u64, Error> {
1534 let execution_cost = invoke_context.get_execution_cost();
1535
1536 consume_compute_meter(invoke_context, execution_cost.syscall_base_cost)?;
1537
1538 let stack_height = invoke_context.get_stack_height();
1541 let instruction_trace_length = invoke_context
1542 .transaction_context
1543 .get_instruction_trace_length();
1544 let mut reverse_index_at_stack_height = 0;
1545 let mut found_instruction_context = None;
1546 for index_in_trace in (0..instruction_trace_length).rev() {
1547 let instruction_context = invoke_context
1548 .transaction_context
1549 .get_instruction_context_at_index_in_trace(index_in_trace)?;
1550 if instruction_context.get_stack_height() < stack_height {
1551 break;
1552 }
1553 if instruction_context.get_stack_height() == stack_height {
1554 if index.saturating_add(1) == reverse_index_at_stack_height {
1555 found_instruction_context = Some(instruction_context);
1556 break;
1557 }
1558 reverse_index_at_stack_height = reverse_index_at_stack_height.saturating_add(1);
1559 }
1560 }
1561
1562 if let Some(instruction_context) = found_instruction_context {
1563 translate_mut!(
1564 memory_mapping,
1565 invoke_context.get_check_aligned(),
1566 let result_header: &mut ProcessedSiblingInstruction = map(meta_addr)?;
1567 );
1568
1569 if result_header.data_len == (instruction_context.get_instruction_data().len() as u64)
1570 && result_header.accounts_len
1571 == (instruction_context.get_number_of_instruction_accounts() as u64)
1572 {
1573 translate_mut!(
1574 memory_mapping,
1575 invoke_context.get_check_aligned(),
1576 let program_id: &mut Pubkey = map(program_id_addr)?;
1577 let data: &mut [u8] = map(data_addr, result_header.data_len)?;
1578 let accounts: &mut [AccountMeta] = map(accounts_addr, result_header.accounts_len)?;
1579 let result_header: &mut ProcessedSiblingInstruction = map(meta_addr)?;
1580 );
1581 let _ = result_header;
1583
1584 *program_id = *instruction_context
1585 .get_program_key()?;
1586 data.clone_from_slice(instruction_context.get_instruction_data());
1587 let account_metas = (0..instruction_context.get_number_of_instruction_accounts())
1588 .map(|instruction_account_index| {
1589 Ok(AccountMeta {
1590 pubkey: *instruction_context.get_key_of_instruction_account(instruction_account_index)?,
1591 is_signer: instruction_context
1592 .is_instruction_account_signer(instruction_account_index)?,
1593 is_writable: instruction_context
1594 .is_instruction_account_writable(instruction_account_index)?,
1595 })
1596 })
1597 .collect::<Result<Vec<_>, InstructionError>>()?;
1598 accounts.clone_from_slice(account_metas.as_slice());
1599 } else {
1600 result_header.data_len = instruction_context.get_instruction_data().len() as u64;
1601 result_header.accounts_len =
1602 instruction_context.get_number_of_instruction_accounts() as u64;
1603 }
1604 return Ok(true as u64);
1605 }
1606 Ok(false as u64)
1607 }
1608);
1609
1610declare_builtin_function!(
1611 SyscallGetStackHeight,
1613 fn rust(
1614 invoke_context: &mut InvokeContext,
1615 _arg1: u64,
1616 _arg2: u64,
1617 _arg3: u64,
1618 _arg4: u64,
1619 _arg5: u64,
1620 _memory_mapping: &mut MemoryMapping,
1621 ) -> Result<u64, Error> {
1622 let execution_cost = invoke_context.get_execution_cost();
1623
1624 consume_compute_meter(invoke_context, execution_cost.syscall_base_cost)?;
1625
1626 Ok(invoke_context.get_stack_height() as u64)
1627 }
1628);
1629
1630declare_builtin_function!(
1631 SyscallAltBn128,
1633 fn rust(
1634 invoke_context: &mut InvokeContext,
1635 group_op: u64,
1636 input_addr: u64,
1637 input_size: u64,
1638 result_addr: u64,
1639 _arg5: u64,
1640 memory_mapping: &mut MemoryMapping,
1641 ) -> Result<u64, Error> {
1642 use atlas_bn254::prelude::{ALT_BN128_ADD, ALT_BN128_MUL, ALT_BN128_PAIRING};
1643 let execution_cost = invoke_context.get_execution_cost();
1644 let (cost, output): (u64, usize) = match group_op {
1645 ALT_BN128_ADD => (
1646 execution_cost.alt_bn128_addition_cost,
1647 ALT_BN128_ADDITION_OUTPUT_LEN,
1648 ),
1649 ALT_BN128_MUL => (
1650 execution_cost.alt_bn128_multiplication_cost,
1651 ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
1652 ),
1653 ALT_BN128_PAIRING => {
1654 let ele_len = input_size
1655 .checked_div(ALT_BN128_PAIRING_ELEMENT_LEN as u64)
1656 .expect("div by non-zero constant");
1657 let cost = execution_cost
1658 .alt_bn128_pairing_one_pair_cost_first
1659 .saturating_add(
1660 execution_cost
1661 .alt_bn128_pairing_one_pair_cost_other
1662 .saturating_mul(ele_len.saturating_sub(1)),
1663 )
1664 .saturating_add(execution_cost.sha256_base_cost)
1665 .saturating_add(input_size)
1666 .saturating_add(ALT_BN128_PAIRING_OUTPUT_LEN as u64);
1667 (cost, ALT_BN128_PAIRING_OUTPUT_LEN)
1668 }
1669 _ => {
1670 return Err(SyscallError::InvalidAttribute.into());
1671 }
1672 };
1673
1674 consume_compute_meter(invoke_context, cost)?;
1675
1676 translate_mut!(
1677 memory_mapping,
1678 invoke_context.get_check_aligned(),
1679 let call_result: &mut [u8] = map(result_addr, output as u64)?;
1680 );
1681 let input = translate_slice::<u8>(
1682 memory_mapping,
1683 input_addr,
1684 input_size,
1685 invoke_context.get_check_aligned(),
1686 )?;
1687
1688 let calculation = match group_op {
1689 ALT_BN128_ADD => alt_bn128_addition,
1690 ALT_BN128_MUL => {
1691 let fix_alt_bn128_multiplication_input_length = invoke_context
1692 .get_feature_set()
1693 .fix_alt_bn128_multiplication_input_length;
1694 if fix_alt_bn128_multiplication_input_length {
1695 alt_bn128_multiplication
1696 } else {
1697 alt_bn128_multiplication_128
1698 }
1699 }
1700 ALT_BN128_PAIRING => alt_bn128_pairing,
1701 _ => {
1702 return Err(SyscallError::InvalidAttribute.into());
1703 }
1704 };
1705
1706 let simplify_alt_bn128_syscall_error_codes = invoke_context
1707 .get_feature_set()
1708 .simplify_alt_bn128_syscall_error_codes;
1709
1710 let result_point = match calculation(input) {
1711 Ok(result_point) => result_point,
1712 Err(e) => {
1713 return if simplify_alt_bn128_syscall_error_codes {
1714 Ok(1)
1715 } else {
1716 Ok(e.into())
1717 };
1718 }
1719 };
1720
1721 if result_point.len() != output && !simplify_alt_bn128_syscall_error_codes {
1724 return Ok(AltBn128Error::SliceOutOfBounds.into());
1725 }
1726
1727 call_result.copy_from_slice(&result_point);
1728 Ok(SUCCESS)
1729 }
1730);
1731
1732declare_builtin_function!(
1733 SyscallBigModExp,
1735 fn rust(
1736 invoke_context: &mut InvokeContext,
1737 params: u64,
1738 return_value: u64,
1739 _arg3: u64,
1740 _arg4: u64,
1741 _arg5: u64,
1742 memory_mapping: &mut MemoryMapping,
1743 ) -> Result<u64, Error> {
1744 let params = &translate_slice::<BigModExpParams>(
1745 memory_mapping,
1746 params,
1747 1,
1748 invoke_context.get_check_aligned(),
1749 )?
1750 .first()
1751 .ok_or(SyscallError::InvalidLength)?;
1752
1753 if params.base_len > 512 || params.exponent_len > 512 || params.modulus_len > 512 {
1754 return Err(Box::new(SyscallError::InvalidLength));
1755 }
1756
1757 let input_len: u64 = std::cmp::max(params.base_len, params.exponent_len);
1758 let input_len: u64 = std::cmp::max(input_len, params.modulus_len);
1759
1760 let execution_cost = invoke_context.get_execution_cost();
1761 consume_compute_meter(
1763 invoke_context,
1764 execution_cost.syscall_base_cost.saturating_add(
1765 input_len
1766 .saturating_mul(input_len)
1767 .checked_div(execution_cost.big_modular_exponentiation_cost_divisor)
1768 .unwrap_or(u64::MAX)
1769 .saturating_add(execution_cost.big_modular_exponentiation_base_cost),
1770 ),
1771 )?;
1772
1773 let base = translate_slice::<u8>(
1774 memory_mapping,
1775 params.base as *const _ as u64,
1776 params.base_len,
1777 invoke_context.get_check_aligned(),
1778 )?;
1779
1780 let exponent = translate_slice::<u8>(
1781 memory_mapping,
1782 params.exponent as *const _ as u64,
1783 params.exponent_len,
1784 invoke_context.get_check_aligned(),
1785 )?;
1786
1787 let modulus = translate_slice::<u8>(
1788 memory_mapping,
1789 params.modulus as *const _ as u64,
1790 params.modulus_len,
1791 invoke_context.get_check_aligned(),
1792 )?;
1793
1794 let value = big_mod_exp(base, exponent, modulus);
1795
1796 translate_mut!(
1797 memory_mapping,
1798 invoke_context.get_check_aligned(),
1799 let return_value_ref_mut: &mut [u8] = map(return_value, params.modulus_len)?;
1800 );
1801 return_value_ref_mut.copy_from_slice(value.as_slice());
1802
1803 Ok(0)
1804 }
1805);
1806
1807declare_builtin_function!(
1808 SyscallPoseidon,
1810 fn rust(
1811 invoke_context: &mut InvokeContext,
1812 parameters: u64,
1813 endianness: u64,
1814 vals_addr: u64,
1815 vals_len: u64,
1816 result_addr: u64,
1817 memory_mapping: &mut MemoryMapping,
1818 ) -> Result<u64, Error> {
1819 let parameters: poseidon::Parameters = parameters.try_into()?;
1820 let endianness: poseidon::Endianness = endianness.try_into()?;
1821
1822 if vals_len > 12 {
1823 ic_msg!(
1824 invoke_context,
1825 "Poseidon hashing {} sequences is not supported",
1826 vals_len,
1827 );
1828 return Err(SyscallError::InvalidLength.into());
1829 }
1830
1831 let execution_cost = invoke_context.get_execution_cost();
1832 let Some(cost) = execution_cost.poseidon_cost(vals_len) else {
1833 ic_msg!(
1834 invoke_context,
1835 "Overflow while calculating the compute cost"
1836 );
1837 return Err(SyscallError::ArithmeticOverflow.into());
1838 };
1839 consume_compute_meter(invoke_context, cost.to_owned())?;
1840
1841 translate_mut!(
1842 memory_mapping,
1843 invoke_context.get_check_aligned(),
1844 let hash_result: &mut [u8] = map(result_addr, poseidon::HASH_BYTES as u64)?;
1845 );
1846 let inputs = translate_slice::<VmSlice<u8>>(
1847 memory_mapping,
1848 vals_addr,
1849 vals_len,
1850 invoke_context.get_check_aligned(),
1851 )?;
1852 let inputs = inputs
1853 .iter()
1854 .map(|input| input.translate(memory_mapping, invoke_context.get_check_aligned()))
1855 .collect::<Result<Vec<_>, Error>>()?;
1856
1857 let simplify_alt_bn128_syscall_error_codes = invoke_context
1858 .get_feature_set()
1859 .simplify_alt_bn128_syscall_error_codes;
1860
1861 let hash = match poseidon::hashv(parameters, endianness, inputs.as_slice()) {
1862 Ok(hash) => hash,
1863 Err(e) => {
1864 return if simplify_alt_bn128_syscall_error_codes {
1865 Ok(1)
1866 } else {
1867 Ok(e.into())
1868 };
1869 }
1870 };
1871 hash_result.copy_from_slice(&hash.to_bytes());
1872
1873 Ok(SUCCESS)
1874 }
1875);
1876
1877declare_builtin_function!(
1878 SyscallRemainingComputeUnits,
1880 fn rust(
1881 invoke_context: &mut InvokeContext,
1882 _arg1: u64,
1883 _arg2: u64,
1884 _arg3: u64,
1885 _arg4: u64,
1886 _arg5: u64,
1887 _memory_mapping: &mut MemoryMapping,
1888 ) -> Result<u64, Error> {
1889 let execution_cost = invoke_context.get_execution_cost();
1890 consume_compute_meter(invoke_context, execution_cost.syscall_base_cost)?;
1891
1892 use atlas_sbpf::vm::ContextObject;
1893 Ok(invoke_context.get_remaining())
1894 }
1895);
1896
1897declare_builtin_function!(
1898 SyscallAltBn128Compression,
1900 fn rust(
1901 invoke_context: &mut InvokeContext,
1902 op: u64,
1903 input_addr: u64,
1904 input_size: u64,
1905 result_addr: u64,
1906 _arg5: u64,
1907 memory_mapping: &mut MemoryMapping,
1908 ) -> Result<u64, Error> {
1909 use atlas_bn254::compression::prelude::{
1910 alt_bn128_g1_compress, alt_bn128_g1_decompress, alt_bn128_g2_compress,
1911 alt_bn128_g2_decompress, ALT_BN128_G1_COMPRESS, ALT_BN128_G1_DECOMPRESS,
1912 ALT_BN128_G2_COMPRESS, ALT_BN128_G2_DECOMPRESS, G1, G1_COMPRESSED, G2, G2_COMPRESSED,
1913 };
1914 let execution_cost = invoke_context.get_execution_cost();
1915 let base_cost = execution_cost.syscall_base_cost;
1916 let (cost, output): (u64, usize) = match op {
1917 ALT_BN128_G1_COMPRESS => (
1918 base_cost.saturating_add(execution_cost.alt_bn128_g1_compress),
1919 G1_COMPRESSED,
1920 ),
1921 ALT_BN128_G1_DECOMPRESS => {
1922 (base_cost.saturating_add(execution_cost.alt_bn128_g1_decompress), G1)
1923 }
1924 ALT_BN128_G2_COMPRESS => (
1925 base_cost.saturating_add(execution_cost.alt_bn128_g2_compress),
1926 G2_COMPRESSED,
1927 ),
1928 ALT_BN128_G2_DECOMPRESS => {
1929 (base_cost.saturating_add(execution_cost.alt_bn128_g2_decompress), G2)
1930 }
1931 _ => {
1932 return Err(SyscallError::InvalidAttribute.into());
1933 }
1934 };
1935
1936 consume_compute_meter(invoke_context, cost)?;
1937
1938 translate_mut!(
1939 memory_mapping,
1940 invoke_context.get_check_aligned(),
1941 let call_result: &mut [u8] = map(result_addr, output as u64)?;
1942 );
1943 let input = translate_slice::<u8>(
1944 memory_mapping,
1945 input_addr,
1946 input_size,
1947 invoke_context.get_check_aligned(),
1948 )?;
1949
1950 let simplify_alt_bn128_syscall_error_codes = invoke_context
1951 .get_feature_set()
1952 .simplify_alt_bn128_syscall_error_codes;
1953
1954 match op {
1955 ALT_BN128_G1_COMPRESS => {
1956 let result_point = match alt_bn128_g1_compress(input) {
1957 Ok(result_point) => result_point,
1958 Err(e) => {
1959 return if simplify_alt_bn128_syscall_error_codes {
1960 Ok(1)
1961 } else {
1962 Ok(e.into())
1963 };
1964 }
1965 };
1966 call_result.copy_from_slice(&result_point);
1967 Ok(SUCCESS)
1968 }
1969 ALT_BN128_G1_DECOMPRESS => {
1970 let result_point = match alt_bn128_g1_decompress(input) {
1971 Ok(result_point) => result_point,
1972 Err(e) => {
1973 return if simplify_alt_bn128_syscall_error_codes {
1974 Ok(1)
1975 } else {
1976 Ok(e.into())
1977 };
1978 }
1979 };
1980 call_result.copy_from_slice(&result_point);
1981 Ok(SUCCESS)
1982 }
1983 ALT_BN128_G2_COMPRESS => {
1984 let result_point = match alt_bn128_g2_compress(input) {
1985 Ok(result_point) => result_point,
1986 Err(e) => {
1987 return if simplify_alt_bn128_syscall_error_codes {
1988 Ok(1)
1989 } else {
1990 Ok(e.into())
1991 };
1992 }
1993 };
1994 call_result.copy_from_slice(&result_point);
1995 Ok(SUCCESS)
1996 }
1997 ALT_BN128_G2_DECOMPRESS => {
1998 let result_point = match alt_bn128_g2_decompress(input) {
1999 Ok(result_point) => result_point,
2000 Err(e) => {
2001 return if simplify_alt_bn128_syscall_error_codes {
2002 Ok(1)
2003 } else {
2004 Ok(e.into())
2005 };
2006 }
2007 };
2008 call_result.copy_from_slice(&result_point);
2009 Ok(SUCCESS)
2010 }
2011 _ => Err(SyscallError::InvalidAttribute.into()),
2012 }
2013 }
2014);
2015
2016declare_builtin_function!(
2017 SyscallHash<H: HasherImpl>,
2019 fn rust(
2020 invoke_context: &mut InvokeContext,
2021 vals_addr: u64,
2022 vals_len: u64,
2023 result_addr: u64,
2024 _arg4: u64,
2025 _arg5: u64,
2026 memory_mapping: &mut MemoryMapping,
2027 ) -> Result<u64, Error> {
2028 let compute_budget = invoke_context.get_compute_budget();
2029 let compute_cost = invoke_context.get_execution_cost();
2030 let hash_base_cost = H::get_base_cost(compute_cost);
2031 let hash_byte_cost = H::get_byte_cost(compute_cost);
2032 let hash_max_slices = H::get_max_slices(compute_budget);
2033 if hash_max_slices < vals_len {
2034 ic_msg!(
2035 invoke_context,
2036 "{} Hashing {} sequences in one syscall is over the limit {}",
2037 H::NAME,
2038 vals_len,
2039 hash_max_slices,
2040 );
2041 return Err(SyscallError::TooManySlices.into());
2042 }
2043
2044 consume_compute_meter(invoke_context, hash_base_cost)?;
2045
2046 translate_mut!(
2047 memory_mapping,
2048 invoke_context.get_check_aligned(),
2049 let hash_result: &mut [u8] = map(result_addr, std::mem::size_of::<H::Output>() as u64)?;
2050 );
2051 let mut hasher = H::create_hasher();
2052 if vals_len > 0 {
2053 let vals = translate_slice::<VmSlice<u8>>(
2054 memory_mapping,
2055 vals_addr,
2056 vals_len,
2057 invoke_context.get_check_aligned(),
2058 )?;
2059
2060 for val in vals.iter() {
2061 let bytes = val.translate(memory_mapping, invoke_context.get_check_aligned())?;
2062 let cost = compute_cost.mem_op_base_cost.max(
2063 hash_byte_cost.saturating_mul(
2064 val.len()
2065 .checked_div(2)
2066 .expect("div by non-zero literal"),
2067 ),
2068 );
2069 consume_compute_meter(invoke_context, cost)?;
2070 hasher.hash(bytes);
2071 }
2072 }
2073 hash_result.copy_from_slice(hasher.result().as_ref());
2074 Ok(0)
2075 }
2076);
2077
2078declare_builtin_function!(
2079 SyscallGetEpochStake,
2081 fn rust(
2082 invoke_context: &mut InvokeContext,
2083 var_addr: u64,
2084 _arg2: u64,
2085 _arg3: u64,
2086 _arg4: u64,
2087 _arg5: u64,
2088 memory_mapping: &mut MemoryMapping,
2089 ) -> Result<u64, Error> {
2090 let compute_cost = invoke_context.get_execution_cost();
2091
2092 if var_addr == 0 {
2093 let compute_units = compute_cost.syscall_base_cost;
2101 consume_compute_meter(invoke_context, compute_units)?;
2102 Ok(invoke_context.get_epoch_stake())
2110 } else {
2111 let compute_units = compute_cost
2119 .syscall_base_cost
2120 .saturating_add(
2121 (PUBKEY_BYTES as u64)
2122 .checked_div(compute_cost.cpi_bytes_per_unit)
2123 .unwrap_or(u64::MAX),
2124 )
2125 .saturating_add(compute_cost.mem_op_base_cost);
2126 consume_compute_meter(invoke_context, compute_units)?;
2127 let check_aligned = invoke_context.get_check_aligned();
2139 let vote_address = translate_type::<Pubkey>(memory_mapping, var_addr, check_aligned)?;
2140
2141 Ok(invoke_context.get_epoch_stake_for_vote_account(vote_address))
2142 }
2143 }
2144);
2145
2146#[cfg(test)]
2147#[allow(clippy::arithmetic_side_effects)]
2148#[allow(clippy::indexing_slicing)]
2149mod tests {
2150 #[allow(deprecated)]
2151 use atlas_sysvar::fees::Fees;
2152 use {
2153 super::*,
2154 assert_matches::assert_matches,
2155 core::slice,
2156 atlas_account::{create_account_shared_data_for_test, AccountSharedData},
2157 atlas_clock::Clock,
2158 atlas_epoch_rewards::EpochRewards,
2159 atlas_epoch_schedule::EpochSchedule,
2160 atlas_fee_calculator::FeeCalculator,
2161 atlas_hash::HASH_BYTES,
2162 atlas_instruction::Instruction,
2163 atlas_last_restart_slot::LastRestartSlot,
2164 atlas_program::program::check_type_assumptions,
2165 atlas_program_runtime::{
2166 execution_budget::MAX_HEAP_FRAME_BYTES,
2167 invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
2168 with_mock_invoke_context,
2169 },
2170 atlas_sbpf::{
2171 aligned_memory::AlignedMemory,
2172 ebpf::{self, HOST_ALIGN},
2173 error::EbpfError,
2174 memory_region::{MemoryMapping, MemoryRegion},
2175 program::SBPFVersion,
2176 vm::Config,
2177 },
2178 atlas_sdk_ids::{bpf_loader, bpf_loader_upgradeable, sysvar},
2179 atlas_sha256_hasher::hashv,
2180 atlas_slot_hashes::{self as slot_hashes, SlotHashes},
2181 atlas_stable_layout::stable_instruction::StableInstruction,
2182 atlas_stake_interface::stake_history::{self, StakeHistory, StakeHistoryEntry},
2183 atlas_sysvar_id::SysvarId,
2184 atlas_transaction_context::InstructionAccount,
2185 std::{
2186 hash::{DefaultHasher, Hash, Hasher},
2187 mem,
2188 str::FromStr,
2189 },
2190 test_case::test_case,
2191 };
2192
2193 macro_rules! assert_access_violation {
2194 ($result:expr, $va:expr, $len:expr) => {
2195 match $result.unwrap_err().downcast_ref::<EbpfError>().unwrap() {
2196 EbpfError::AccessViolation(_, va, len, _) if $va == *va && $len == *len => {}
2197 EbpfError::StackAccessViolation(_, va, len, _) if $va == *va && $len == *len => {}
2198 _ => panic!(),
2199 }
2200 };
2201 }
2202
2203 macro_rules! prepare_mockup {
2204 ($invoke_context:ident,
2205 $program_key:ident,
2206 $loader_key:expr $(,)?) => {
2207 let $program_key = Pubkey::new_unique();
2208 let transaction_accounts = vec![
2209 (
2210 $loader_key,
2211 AccountSharedData::new(0, 0, &native_loader::id()),
2212 ),
2213 ($program_key, AccountSharedData::new(0, 0, &$loader_key)),
2214 ];
2215 with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
2216 $invoke_context
2217 .transaction_context
2218 .configure_next_instruction_for_tests(1, vec![], &[])
2219 .unwrap();
2220 $invoke_context.push().unwrap();
2221 };
2222 }
2223
2224 #[allow(dead_code)]
2225 struct MockSlice {
2226 vm_addr: u64,
2227 len: usize,
2228 }
2229
2230 #[test]
2231 fn test_translate() {
2232 const START: u64 = 0x100000000;
2233 const LENGTH: u64 = 1000;
2234
2235 let data = vec![0u8; LENGTH as usize];
2236 let addr = data.as_ptr() as u64;
2237 let config = Config::default();
2238 let memory_mapping = MemoryMapping::new(
2239 vec![MemoryRegion::new_readonly(&data, START)],
2240 &config,
2241 SBPFVersion::V3,
2242 )
2243 .unwrap();
2244
2245 let cases = vec![
2246 (true, START, 0, addr),
2247 (true, START, 1, addr),
2248 (true, START, LENGTH, addr),
2249 (true, START + 1, LENGTH - 1, addr + 1),
2250 (false, START + 1, LENGTH, 0),
2251 (true, START + LENGTH - 1, 1, addr + LENGTH - 1),
2252 (true, START + LENGTH, 0, addr + LENGTH),
2253 (false, START + LENGTH, 1, 0),
2254 (false, START, LENGTH + 1, 0),
2255 (false, 0, 0, 0),
2256 (false, 0, 1, 0),
2257 (false, START - 1, 0, 0),
2258 (false, START - 1, 1, 0),
2259 (true, START + LENGTH / 2, LENGTH / 2, addr + LENGTH / 2),
2260 ];
2261 for (ok, start, length, value) in cases {
2262 if ok {
2263 assert_eq!(
2264 translate_inner!(&memory_mapping, map, AccessType::Load, start, length)
2265 .unwrap(),
2266 value
2267 )
2268 } else {
2269 assert!(
2270 translate_inner!(&memory_mapping, map, AccessType::Load, start, length)
2271 .is_err()
2272 )
2273 }
2274 }
2275 }
2276
2277 #[test]
2278 fn test_translate_type() {
2279 let config = Config::default();
2280
2281 let pubkey = atlas_pubkey::new_rand();
2283 let memory_mapping = MemoryMapping::new(
2284 vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
2285 &config,
2286 SBPFVersion::V3,
2287 )
2288 .unwrap();
2289 let translated_pubkey =
2290 translate_type::<Pubkey>(&memory_mapping, 0x100000000, true).unwrap();
2291 assert_eq!(pubkey, *translated_pubkey);
2292
2293 let instruction = Instruction::new_with_bincode(
2295 atlas_pubkey::new_rand(),
2296 &"foobar",
2297 vec![AccountMeta::new(atlas_pubkey::new_rand(), false)],
2298 );
2299 let instruction = StableInstruction::from(instruction);
2300 let memory_region = MemoryRegion::new_readonly(bytes_of(&instruction), 0x100000000);
2301 let memory_mapping =
2302 MemoryMapping::new(vec![memory_region], &config, SBPFVersion::V3).unwrap();
2303 let translated_instruction =
2304 translate_type::<StableInstruction>(&memory_mapping, 0x100000000, true).unwrap();
2305 assert_eq!(instruction, *translated_instruction);
2306
2307 let memory_region = MemoryRegion::new_readonly(&bytes_of(&instruction)[..1], 0x100000000);
2308 let memory_mapping =
2309 MemoryMapping::new(vec![memory_region], &config, SBPFVersion::V3).unwrap();
2310 assert!(translate_type::<Instruction>(&memory_mapping, 0x100000000, true).is_err());
2311 }
2312
2313 #[test]
2314 fn test_translate_slice() {
2315 let config = Config::default();
2316
2317 let good_data = vec![1u8, 2, 3, 4, 5];
2319 let data: Vec<u8> = vec![];
2320 assert_eq!(std::ptr::dangling::<u8>(), data.as_ptr());
2321 let memory_mapping = MemoryMapping::new(
2322 vec![MemoryRegion::new_readonly(&good_data, 0x100000000)],
2323 &config,
2324 SBPFVersion::V3,
2325 )
2326 .unwrap();
2327 let translated_data =
2328 translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, 0, true).unwrap();
2329 assert_eq!(data, translated_data);
2330 assert_eq!(0, translated_data.len());
2331
2332 let mut data = vec![1u8, 2, 3, 4, 5];
2334 let memory_mapping = MemoryMapping::new(
2335 vec![MemoryRegion::new_readonly(&data, 0x100000000)],
2336 &config,
2337 SBPFVersion::V3,
2338 )
2339 .unwrap();
2340 let translated_data =
2341 translate_slice::<u8>(&memory_mapping, 0x100000000, data.len() as u64, true).unwrap();
2342 assert_eq!(data, translated_data);
2343 *data.first_mut().unwrap() = 10;
2344 assert_eq!(data, translated_data);
2345 assert!(
2346 translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, u64::MAX, true).is_err()
2347 );
2348
2349 assert!(
2350 translate_slice::<u8>(&memory_mapping, 0x100000000 - 1, data.len() as u64, true,)
2351 .is_err()
2352 );
2353
2354 let mut data = vec![1u64, 2, 3, 4, 5];
2356 let memory_mapping = MemoryMapping::new(
2357 vec![MemoryRegion::new_readonly(
2358 bytes_of_slice(&data),
2359 0x100000000,
2360 )],
2361 &config,
2362 SBPFVersion::V3,
2363 )
2364 .unwrap();
2365 let translated_data =
2366 translate_slice::<u64>(&memory_mapping, 0x100000000, data.len() as u64, true).unwrap();
2367 assert_eq!(data, translated_data);
2368 *data.first_mut().unwrap() = 10;
2369 assert_eq!(data, translated_data);
2370 assert!(translate_slice::<u64>(&memory_mapping, 0x100000000, u64::MAX, true).is_err());
2371
2372 let mut data = vec![atlas_pubkey::new_rand(); 5];
2374 let memory_mapping = MemoryMapping::new(
2375 vec![MemoryRegion::new_readonly(
2376 unsafe {
2377 slice::from_raw_parts(data.as_ptr() as *const u8, mem::size_of::<Pubkey>() * 5)
2378 },
2379 0x100000000,
2380 )],
2381 &config,
2382 SBPFVersion::V3,
2383 )
2384 .unwrap();
2385 let translated_data =
2386 translate_slice::<Pubkey>(&memory_mapping, 0x100000000, data.len() as u64, true)
2387 .unwrap();
2388 assert_eq!(data, translated_data);
2389 *data.first_mut().unwrap() = atlas_pubkey::new_rand(); assert_eq!(data, translated_data);
2391 }
2392
2393 #[test]
2394 fn test_translate_string_and_do() {
2395 let string = "Gaggablaghblagh!";
2396 let config = Config::default();
2397 let memory_mapping = MemoryMapping::new(
2398 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2399 &config,
2400 SBPFVersion::V3,
2401 )
2402 .unwrap();
2403 assert_eq!(
2404 42,
2405 translate_string_and_do(
2406 &memory_mapping,
2407 0x100000000,
2408 string.len() as u64,
2409 true,
2410 &mut |string: &str| {
2411 assert_eq!(string, "Gaggablaghblagh!");
2412 Ok(42)
2413 }
2414 )
2415 .unwrap()
2416 );
2417 }
2418
2419 #[test]
2420 #[should_panic(expected = "Abort")]
2421 fn test_syscall_abort() {
2422 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2423 let config = Config::default();
2424 let mut memory_mapping = MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap();
2425 let result = SyscallAbort::rust(&mut invoke_context, 0, 0, 0, 0, 0, &mut memory_mapping);
2426 result.unwrap();
2427 }
2428
2429 #[test]
2430 #[should_panic(expected = "Panic(\"Gaggablaghblagh!\", 42, 84)")]
2431 fn test_syscall_sol_panic() {
2432 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2433
2434 let string = "Gaggablaghblagh!";
2435 let config = Config::default();
2436 let mut memory_mapping = MemoryMapping::new(
2437 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2438 &config,
2439 SBPFVersion::V3,
2440 )
2441 .unwrap();
2442
2443 invoke_context.mock_set_remaining(string.len() as u64 - 1);
2444 let result = SyscallPanic::rust(
2445 &mut invoke_context,
2446 0x100000000,
2447 string.len() as u64,
2448 42,
2449 84,
2450 0,
2451 &mut memory_mapping,
2452 );
2453 assert_matches!(
2454 result,
2455 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2456 );
2457
2458 invoke_context.mock_set_remaining(string.len() as u64);
2459 let result = SyscallPanic::rust(
2460 &mut invoke_context,
2461 0x100000000,
2462 string.len() as u64,
2463 42,
2464 84,
2465 0,
2466 &mut memory_mapping,
2467 );
2468 result.unwrap();
2469 }
2470
2471 #[test]
2472 fn test_syscall_sol_log() {
2473 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2474
2475 let string = "Gaggablaghblagh!";
2476 let config = Config::default();
2477 let mut memory_mapping = MemoryMapping::new(
2478 vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
2479 &config,
2480 SBPFVersion::V3,
2481 )
2482 .unwrap();
2483
2484 invoke_context.mock_set_remaining(400 - 1);
2485 let result = SyscallLog::rust(
2486 &mut invoke_context,
2487 0x100000001, string.len() as u64,
2489 0,
2490 0,
2491 0,
2492 &mut memory_mapping,
2493 );
2494 assert_access_violation!(result, 0x100000001, string.len() as u64);
2495 let result = SyscallLog::rust(
2496 &mut invoke_context,
2497 0x100000000,
2498 string.len() as u64 * 2, 0,
2500 0,
2501 0,
2502 &mut memory_mapping,
2503 );
2504 assert_access_violation!(result, 0x100000000, string.len() as u64 * 2);
2505
2506 let result = SyscallLog::rust(
2507 &mut invoke_context,
2508 0x100000000,
2509 string.len() as u64,
2510 0,
2511 0,
2512 0,
2513 &mut memory_mapping,
2514 );
2515 result.unwrap();
2516 let result = SyscallLog::rust(
2517 &mut invoke_context,
2518 0x100000000,
2519 string.len() as u64,
2520 0,
2521 0,
2522 0,
2523 &mut memory_mapping,
2524 );
2525 assert_matches!(
2526 result,
2527 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2528 );
2529
2530 assert_eq!(
2531 invoke_context
2532 .get_log_collector()
2533 .unwrap()
2534 .borrow()
2535 .get_recorded_content(),
2536 &["Program log: Gaggablaghblagh!".to_string()]
2537 );
2538 }
2539
2540 #[test]
2541 fn test_syscall_sol_log_u64() {
2542 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2543 let cost = invoke_context.get_execution_cost().log_64_units;
2544
2545 invoke_context.mock_set_remaining(cost);
2546 let config = Config::default();
2547 let mut memory_mapping = MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap();
2548 let result = SyscallLogU64::rust(&mut invoke_context, 1, 2, 3, 4, 5, &mut memory_mapping);
2549 result.unwrap();
2550
2551 assert_eq!(
2552 invoke_context
2553 .get_log_collector()
2554 .unwrap()
2555 .borrow()
2556 .get_recorded_content(),
2557 &["Program log: 0x1, 0x2, 0x3, 0x4, 0x5".to_string()]
2558 );
2559 }
2560
2561 #[test]
2562 fn test_syscall_sol_pubkey() {
2563 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2564 let cost = invoke_context.get_execution_cost().log_pubkey_units;
2565
2566 let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap();
2567 let config = Config::default();
2568 let mut memory_mapping = MemoryMapping::new(
2569 vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
2570 &config,
2571 SBPFVersion::V3,
2572 )
2573 .unwrap();
2574
2575 let result = SyscallLogPubkey::rust(
2576 &mut invoke_context,
2577 0x100000001, 32,
2579 0,
2580 0,
2581 0,
2582 &mut memory_mapping,
2583 );
2584 assert_access_violation!(result, 0x100000001, 32);
2585
2586 invoke_context.mock_set_remaining(1);
2587 let result =
2588 SyscallLogPubkey::rust(&mut invoke_context, 100, 32, 0, 0, 0, &mut memory_mapping);
2589 assert_matches!(
2590 result,
2591 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2592 );
2593
2594 invoke_context.mock_set_remaining(cost);
2595 let result = SyscallLogPubkey::rust(
2596 &mut invoke_context,
2597 0x100000000,
2598 0,
2599 0,
2600 0,
2601 0,
2602 &mut memory_mapping,
2603 );
2604 result.unwrap();
2605
2606 assert_eq!(
2607 invoke_context
2608 .get_log_collector()
2609 .unwrap()
2610 .borrow()
2611 .get_recorded_content(),
2612 &["Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN".to_string()]
2613 );
2614 }
2615
2616 macro_rules! setup_alloc_test {
2617 ($invoke_context:ident, $memory_mapping:ident, $heap:ident) => {
2618 prepare_mockup!($invoke_context, program_id, bpf_loader::id());
2619 $invoke_context
2620 .set_syscall_context(SyscallContext {
2621 allocator: BpfAllocator::new(atlas_program_entrypoint::HEAP_LENGTH as u64),
2622 accounts_metadata: Vec::new(),
2623 trace_log: Vec::new(),
2624 })
2625 .unwrap();
2626 let config = Config {
2627 aligned_memory_mapping: false,
2628 ..Config::default()
2629 };
2630 let mut $heap =
2631 AlignedMemory::<{ HOST_ALIGN }>::zero_filled(MAX_HEAP_FRAME_BYTES as usize);
2632 let regions = vec![MemoryRegion::new_writable(
2633 $heap.as_slice_mut(),
2634 ebpf::MM_HEAP_START,
2635 )];
2636 let mut $memory_mapping =
2637 MemoryMapping::new(regions, &config, SBPFVersion::V3).unwrap();
2638 };
2639 }
2640
2641 #[test]
2642 fn test_syscall_sol_alloc_free() {
2643 {
2645 setup_alloc_test!(invoke_context, memory_mapping, heap);
2646 let result = SyscallAllocFree::rust(
2647 &mut invoke_context,
2648 atlas_program_entrypoint::HEAP_LENGTH as u64,
2649 0,
2650 0,
2651 0,
2652 0,
2653 &mut memory_mapping,
2654 );
2655 assert_ne!(result.unwrap(), 0);
2656 let result = SyscallAllocFree::rust(
2657 &mut invoke_context,
2658 atlas_program_entrypoint::HEAP_LENGTH as u64,
2659 0,
2660 0,
2661 0,
2662 0,
2663 &mut memory_mapping,
2664 );
2665 assert_eq!(result.unwrap(), 0);
2666 let result = SyscallAllocFree::rust(
2667 &mut invoke_context,
2668 u64::MAX,
2669 0,
2670 0,
2671 0,
2672 0,
2673 &mut memory_mapping,
2674 );
2675 assert_eq!(result.unwrap(), 0);
2676 }
2677
2678 {
2680 setup_alloc_test!(invoke_context, memory_mapping, heap);
2681 for _ in 0..100 {
2682 let result =
2683 SyscallAllocFree::rust(&mut invoke_context, 1, 0, 0, 0, 0, &mut memory_mapping);
2684 assert_ne!(result.unwrap(), 0);
2685 }
2686 let result = SyscallAllocFree::rust(
2687 &mut invoke_context,
2688 atlas_program_entrypoint::HEAP_LENGTH as u64,
2689 0,
2690 0,
2691 0,
2692 0,
2693 &mut memory_mapping,
2694 );
2695 assert_eq!(result.unwrap(), 0);
2696 }
2697
2698 {
2700 setup_alloc_test!(invoke_context, memory_mapping, heap);
2701 for _ in 0..12 {
2702 let result =
2703 SyscallAllocFree::rust(&mut invoke_context, 1, 0, 0, 0, 0, &mut memory_mapping);
2704 assert_ne!(result.unwrap(), 0);
2705 }
2706 let result = SyscallAllocFree::rust(
2707 &mut invoke_context,
2708 atlas_program_entrypoint::HEAP_LENGTH as u64,
2709 0,
2710 0,
2711 0,
2712 0,
2713 &mut memory_mapping,
2714 );
2715 assert_eq!(result.unwrap(), 0);
2716 }
2717
2718 fn aligned<T>() {
2721 setup_alloc_test!(invoke_context, memory_mapping, heap);
2722 let result = SyscallAllocFree::rust(
2723 &mut invoke_context,
2724 size_of::<T>() as u64,
2725 0,
2726 0,
2727 0,
2728 0,
2729 &mut memory_mapping,
2730 );
2731 let address = result.unwrap();
2732 assert_ne!(address, 0);
2733 assert!(address_is_aligned::<T>(address));
2734 }
2735 aligned::<u8>();
2736 aligned::<u16>();
2737 aligned::<u32>();
2738 aligned::<u64>();
2739 aligned::<u128>();
2740 }
2741
2742 #[test]
2743 fn test_syscall_sha256() {
2744 let config = Config::default();
2745 prepare_mockup!(invoke_context, program_id, bpf_loader_deprecated::id());
2746
2747 let bytes1 = "Gaggablaghblagh!";
2748 let bytes2 = "flurbos";
2749
2750 let mock_slice1 = MockSlice {
2751 vm_addr: 0x300000000,
2752 len: bytes1.len(),
2753 };
2754 let mock_slice2 = MockSlice {
2755 vm_addr: 0x400000000,
2756 len: bytes2.len(),
2757 };
2758 let bytes_to_hash = [mock_slice1, mock_slice2];
2759 let mut hash_result = [0; HASH_BYTES];
2760 let ro_len = bytes_to_hash.len() as u64;
2761 let ro_va = 0x100000000;
2762 let rw_va = 0x200000000;
2763 let mut memory_mapping = MemoryMapping::new(
2764 vec![
2765 MemoryRegion::new_readonly(bytes_of_slice(&bytes_to_hash), ro_va),
2766 MemoryRegion::new_writable(bytes_of_slice_mut(&mut hash_result), rw_va),
2767 MemoryRegion::new_readonly(bytes1.as_bytes(), bytes_to_hash[0].vm_addr),
2768 MemoryRegion::new_readonly(bytes2.as_bytes(), bytes_to_hash[1].vm_addr),
2769 ],
2770 &config,
2771 SBPFVersion::V3,
2772 )
2773 .unwrap();
2774
2775 invoke_context.mock_set_remaining(
2776 (invoke_context.get_execution_cost().sha256_base_cost
2777 + invoke_context.get_execution_cost().mem_op_base_cost.max(
2778 invoke_context
2779 .get_execution_cost()
2780 .sha256_byte_cost
2781 .saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2),
2782 ))
2783 * 4,
2784 );
2785
2786 let result = SyscallHash::rust::<Sha256Hasher>(
2787 &mut invoke_context,
2788 ro_va,
2789 ro_len,
2790 rw_va,
2791 0,
2792 0,
2793 &mut memory_mapping,
2794 );
2795 result.unwrap();
2796
2797 let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes();
2798 assert_eq!(hash_result, hash_local);
2799 let result = SyscallHash::rust::<Sha256Hasher>(
2800 &mut invoke_context,
2801 ro_va - 1, ro_len,
2803 rw_va,
2804 0,
2805 0,
2806 &mut memory_mapping,
2807 );
2808 assert_access_violation!(result, ro_va - 1, 32);
2809 let result = SyscallHash::rust::<Sha256Hasher>(
2810 &mut invoke_context,
2811 ro_va,
2812 ro_len + 1, rw_va,
2814 0,
2815 0,
2816 &mut memory_mapping,
2817 );
2818 assert_access_violation!(result, ro_va, 48);
2819 let result = SyscallHash::rust::<Sha256Hasher>(
2820 &mut invoke_context,
2821 ro_va,
2822 ro_len,
2823 rw_va - 1, 0,
2825 0,
2826 &mut memory_mapping,
2827 );
2828 assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64);
2829 let result = SyscallHash::rust::<Sha256Hasher>(
2830 &mut invoke_context,
2831 ro_va,
2832 ro_len,
2833 rw_va,
2834 0,
2835 0,
2836 &mut memory_mapping,
2837 );
2838 assert_matches!(
2839 result,
2840 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2841 );
2842 }
2843
2844 #[test]
2845 fn test_syscall_edwards_curve_point_validation() {
2846 use atlas_curve25519::curve_syscall_traits::CURVE25519_EDWARDS;
2847
2848 let config = Config::default();
2849 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2850
2851 let valid_bytes: [u8; 32] = [
2852 201, 179, 241, 122, 180, 185, 239, 50, 183, 52, 221, 0, 153, 195, 43, 18, 22, 38, 187,
2853 206, 179, 192, 210, 58, 53, 45, 150, 98, 89, 17, 158, 11,
2854 ];
2855 let valid_bytes_va = 0x100000000;
2856
2857 let invalid_bytes: [u8; 32] = [
2858 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2859 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2860 ];
2861 let invalid_bytes_va = 0x200000000;
2862
2863 let mut memory_mapping = MemoryMapping::new(
2864 vec![
2865 MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
2866 MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
2867 ],
2868 &config,
2869 SBPFVersion::V3,
2870 )
2871 .unwrap();
2872
2873 invoke_context.mock_set_remaining(
2874 (invoke_context
2875 .get_execution_cost()
2876 .curve25519_edwards_validate_point_cost)
2877 * 2,
2878 );
2879
2880 let result = SyscallCurvePointValidation::rust(
2881 &mut invoke_context,
2882 CURVE25519_EDWARDS,
2883 valid_bytes_va,
2884 0,
2885 0,
2886 0,
2887 &mut memory_mapping,
2888 );
2889 assert_eq!(0, result.unwrap());
2890
2891 let result = SyscallCurvePointValidation::rust(
2892 &mut invoke_context,
2893 CURVE25519_EDWARDS,
2894 invalid_bytes_va,
2895 0,
2896 0,
2897 0,
2898 &mut memory_mapping,
2899 );
2900 assert_eq!(1, result.unwrap());
2901
2902 let result = SyscallCurvePointValidation::rust(
2903 &mut invoke_context,
2904 CURVE25519_EDWARDS,
2905 valid_bytes_va,
2906 0,
2907 0,
2908 0,
2909 &mut memory_mapping,
2910 );
2911 assert_matches!(
2912 result,
2913 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2914 );
2915 }
2916
2917 #[test]
2918 fn test_syscall_ristretto_curve_point_validation() {
2919 use atlas_curve25519::curve_syscall_traits::CURVE25519_RISTRETTO;
2920
2921 let config = Config::default();
2922 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2923
2924 let valid_bytes: [u8; 32] = [
2925 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11,
2926 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118,
2927 ];
2928 let valid_bytes_va = 0x100000000;
2929
2930 let invalid_bytes: [u8; 32] = [
2931 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
2932 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
2933 ];
2934 let invalid_bytes_va = 0x200000000;
2935
2936 let mut memory_mapping = MemoryMapping::new(
2937 vec![
2938 MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
2939 MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
2940 ],
2941 &config,
2942 SBPFVersion::V3,
2943 )
2944 .unwrap();
2945
2946 invoke_context.mock_set_remaining(
2947 (invoke_context
2948 .get_execution_cost()
2949 .curve25519_ristretto_validate_point_cost)
2950 * 2,
2951 );
2952
2953 let result = SyscallCurvePointValidation::rust(
2954 &mut invoke_context,
2955 CURVE25519_RISTRETTO,
2956 valid_bytes_va,
2957 0,
2958 0,
2959 0,
2960 &mut memory_mapping,
2961 );
2962 assert_eq!(0, result.unwrap());
2963
2964 let result = SyscallCurvePointValidation::rust(
2965 &mut invoke_context,
2966 CURVE25519_RISTRETTO,
2967 invalid_bytes_va,
2968 0,
2969 0,
2970 0,
2971 &mut memory_mapping,
2972 );
2973 assert_eq!(1, result.unwrap());
2974
2975 let result = SyscallCurvePointValidation::rust(
2976 &mut invoke_context,
2977 CURVE25519_RISTRETTO,
2978 valid_bytes_va,
2979 0,
2980 0,
2981 0,
2982 &mut memory_mapping,
2983 );
2984 assert_matches!(
2985 result,
2986 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
2987 );
2988 }
2989
2990 #[test]
2991 fn test_syscall_edwards_curve_group_ops() {
2992 use atlas_curve25519::curve_syscall_traits::{ADD, CURVE25519_EDWARDS, MUL, SUB};
2993
2994 let config = Config::default();
2995 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2996
2997 let left_point: [u8; 32] = [
2998 33, 124, 71, 170, 117, 69, 151, 247, 59, 12, 95, 125, 133, 166, 64, 5, 2, 27, 90, 27,
2999 200, 167, 59, 164, 52, 54, 52, 200, 29, 13, 34, 213,
3000 ];
3001 let left_point_va = 0x100000000;
3002 let right_point: [u8; 32] = [
3003 70, 222, 137, 221, 253, 204, 71, 51, 78, 8, 124, 1, 67, 200, 102, 225, 122, 228, 111,
3004 183, 129, 14, 131, 210, 212, 95, 109, 246, 55, 10, 159, 91,
3005 ];
3006 let right_point_va = 0x200000000;
3007 let scalar: [u8; 32] = [
3008 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3009 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3010 ];
3011 let scalar_va = 0x300000000;
3012 let invalid_point: [u8; 32] = [
3013 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
3014 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
3015 ];
3016 let invalid_point_va = 0x400000000;
3017 let mut result_point: [u8; 32] = [0; 32];
3018 let result_point_va = 0x500000000;
3019
3020 let mut memory_mapping = MemoryMapping::new(
3021 vec![
3022 MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
3023 MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
3024 MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
3025 MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
3026 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3027 ],
3028 &config,
3029 SBPFVersion::V3,
3030 )
3031 .unwrap();
3032
3033 invoke_context.mock_set_remaining(
3034 (invoke_context
3035 .get_execution_cost()
3036 .curve25519_edwards_add_cost
3037 + invoke_context
3038 .get_execution_cost()
3039 .curve25519_edwards_subtract_cost
3040 + invoke_context
3041 .get_execution_cost()
3042 .curve25519_edwards_multiply_cost)
3043 * 2,
3044 );
3045
3046 let result = SyscallCurveGroupOps::rust(
3047 &mut invoke_context,
3048 CURVE25519_EDWARDS,
3049 ADD,
3050 left_point_va,
3051 right_point_va,
3052 result_point_va,
3053 &mut memory_mapping,
3054 );
3055
3056 assert_eq!(0, result.unwrap());
3057 let expected_sum = [
3058 7, 251, 187, 86, 186, 232, 57, 242, 193, 236, 49, 200, 90, 29, 254, 82, 46, 80, 83, 70,
3059 244, 153, 23, 156, 2, 138, 207, 51, 165, 38, 200, 85,
3060 ];
3061 assert_eq!(expected_sum, result_point);
3062
3063 let result = SyscallCurveGroupOps::rust(
3064 &mut invoke_context,
3065 CURVE25519_EDWARDS,
3066 ADD,
3067 invalid_point_va,
3068 right_point_va,
3069 result_point_va,
3070 &mut memory_mapping,
3071 );
3072 assert_eq!(1, result.unwrap());
3073
3074 let result = SyscallCurveGroupOps::rust(
3075 &mut invoke_context,
3076 CURVE25519_EDWARDS,
3077 SUB,
3078 left_point_va,
3079 right_point_va,
3080 result_point_va,
3081 &mut memory_mapping,
3082 );
3083
3084 assert_eq!(0, result.unwrap());
3085 let expected_difference = [
3086 60, 87, 90, 68, 232, 25, 7, 172, 247, 120, 158, 104, 52, 127, 94, 244, 5, 79, 253, 15,
3087 48, 69, 82, 134, 155, 70, 188, 81, 108, 95, 212, 9,
3088 ];
3089 assert_eq!(expected_difference, result_point);
3090
3091 let result = SyscallCurveGroupOps::rust(
3092 &mut invoke_context,
3093 CURVE25519_EDWARDS,
3094 SUB,
3095 invalid_point_va,
3096 right_point_va,
3097 result_point_va,
3098 &mut memory_mapping,
3099 );
3100 assert_eq!(1, result.unwrap());
3101
3102 let result = SyscallCurveGroupOps::rust(
3103 &mut invoke_context,
3104 CURVE25519_EDWARDS,
3105 MUL,
3106 scalar_va,
3107 right_point_va,
3108 result_point_va,
3109 &mut memory_mapping,
3110 );
3111
3112 result.unwrap();
3113 let expected_product = [
3114 64, 150, 40, 55, 80, 49, 217, 209, 105, 229, 181, 65, 241, 68, 2, 106, 220, 234, 211,
3115 71, 159, 76, 156, 114, 242, 68, 147, 31, 243, 211, 191, 124,
3116 ];
3117 assert_eq!(expected_product, result_point);
3118
3119 let result = SyscallCurveGroupOps::rust(
3120 &mut invoke_context,
3121 CURVE25519_EDWARDS,
3122 MUL,
3123 scalar_va,
3124 invalid_point_va,
3125 result_point_va,
3126 &mut memory_mapping,
3127 );
3128 assert_eq!(1, result.unwrap());
3129
3130 let result = SyscallCurveGroupOps::rust(
3131 &mut invoke_context,
3132 CURVE25519_EDWARDS,
3133 MUL,
3134 scalar_va,
3135 invalid_point_va,
3136 result_point_va,
3137 &mut memory_mapping,
3138 );
3139 assert_matches!(
3140 result,
3141 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
3142 );
3143 }
3144
3145 #[test]
3146 fn test_syscall_ristretto_curve_group_ops() {
3147 use atlas_curve25519::curve_syscall_traits::{ADD, CURVE25519_RISTRETTO, MUL, SUB};
3148
3149 let config = Config::default();
3150 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3151
3152 let left_point: [u8; 32] = [
3153 208, 165, 125, 204, 2, 100, 218, 17, 170, 194, 23, 9, 102, 156, 134, 136, 217, 190, 98,
3154 34, 183, 194, 228, 153, 92, 11, 108, 103, 28, 57, 88, 15,
3155 ];
3156 let left_point_va = 0x100000000;
3157 let right_point: [u8; 32] = [
3158 208, 241, 72, 163, 73, 53, 32, 174, 54, 194, 71, 8, 70, 181, 244, 199, 93, 147, 99,
3159 231, 162, 127, 25, 40, 39, 19, 140, 132, 112, 212, 145, 108,
3160 ];
3161 let right_point_va = 0x200000000;
3162 let scalar: [u8; 32] = [
3163 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3164 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3165 ];
3166 let scalar_va = 0x300000000;
3167 let invalid_point: [u8; 32] = [
3168 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
3169 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
3170 ];
3171 let invalid_point_va = 0x400000000;
3172 let mut result_point: [u8; 32] = [0; 32];
3173 let result_point_va = 0x500000000;
3174
3175 let mut memory_mapping = MemoryMapping::new(
3176 vec![
3177 MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
3178 MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
3179 MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
3180 MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
3181 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3182 ],
3183 &config,
3184 SBPFVersion::V3,
3185 )
3186 .unwrap();
3187
3188 invoke_context.mock_set_remaining(
3189 (invoke_context
3190 .get_execution_cost()
3191 .curve25519_ristretto_add_cost
3192 + invoke_context
3193 .get_execution_cost()
3194 .curve25519_ristretto_subtract_cost
3195 + invoke_context
3196 .get_execution_cost()
3197 .curve25519_ristretto_multiply_cost)
3198 * 2,
3199 );
3200
3201 let result = SyscallCurveGroupOps::rust(
3202 &mut invoke_context,
3203 CURVE25519_RISTRETTO,
3204 ADD,
3205 left_point_va,
3206 right_point_va,
3207 result_point_va,
3208 &mut memory_mapping,
3209 );
3210
3211 assert_eq!(0, result.unwrap());
3212 let expected_sum = [
3213 78, 173, 9, 241, 180, 224, 31, 107, 176, 210, 144, 240, 118, 73, 70, 191, 128, 119,
3214 141, 113, 125, 215, 161, 71, 49, 176, 87, 38, 180, 177, 39, 78,
3215 ];
3216 assert_eq!(expected_sum, result_point);
3217
3218 let result = SyscallCurveGroupOps::rust(
3219 &mut invoke_context,
3220 CURVE25519_RISTRETTO,
3221 ADD,
3222 invalid_point_va,
3223 right_point_va,
3224 result_point_va,
3225 &mut memory_mapping,
3226 );
3227 assert_eq!(1, result.unwrap());
3228
3229 let result = SyscallCurveGroupOps::rust(
3230 &mut invoke_context,
3231 CURVE25519_RISTRETTO,
3232 SUB,
3233 left_point_va,
3234 right_point_va,
3235 result_point_va,
3236 &mut memory_mapping,
3237 );
3238
3239 assert_eq!(0, result.unwrap());
3240 let expected_difference = [
3241 150, 72, 222, 61, 148, 79, 96, 130, 151, 176, 29, 217, 231, 211, 0, 215, 76, 86, 212,
3242 146, 110, 128, 24, 151, 187, 144, 108, 233, 221, 208, 157, 52,
3243 ];
3244 assert_eq!(expected_difference, result_point);
3245
3246 let result = SyscallCurveGroupOps::rust(
3247 &mut invoke_context,
3248 CURVE25519_RISTRETTO,
3249 SUB,
3250 invalid_point_va,
3251 right_point_va,
3252 result_point_va,
3253 &mut memory_mapping,
3254 );
3255
3256 assert_eq!(1, result.unwrap());
3257
3258 let result = SyscallCurveGroupOps::rust(
3259 &mut invoke_context,
3260 CURVE25519_RISTRETTO,
3261 MUL,
3262 scalar_va,
3263 right_point_va,
3264 result_point_va,
3265 &mut memory_mapping,
3266 );
3267
3268 result.unwrap();
3269 let expected_product = [
3270 4, 16, 46, 2, 53, 151, 201, 133, 117, 149, 232, 164, 119, 109, 136, 20, 153, 24, 124,
3271 21, 101, 124, 80, 19, 119, 100, 77, 108, 65, 187, 228, 5,
3272 ];
3273 assert_eq!(expected_product, result_point);
3274
3275 let result = SyscallCurveGroupOps::rust(
3276 &mut invoke_context,
3277 CURVE25519_RISTRETTO,
3278 MUL,
3279 scalar_va,
3280 invalid_point_va,
3281 result_point_va,
3282 &mut memory_mapping,
3283 );
3284
3285 assert_eq!(1, result.unwrap());
3286
3287 let result = SyscallCurveGroupOps::rust(
3288 &mut invoke_context,
3289 CURVE25519_RISTRETTO,
3290 MUL,
3291 scalar_va,
3292 invalid_point_va,
3293 result_point_va,
3294 &mut memory_mapping,
3295 );
3296 assert_matches!(
3297 result,
3298 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
3299 );
3300 }
3301
3302 #[test]
3303 fn test_syscall_multiscalar_multiplication() {
3304 use atlas_curve25519::curve_syscall_traits::{CURVE25519_EDWARDS, CURVE25519_RISTRETTO};
3305
3306 let config = Config::default();
3307 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3308
3309 let scalar_a: [u8; 32] = [
3310 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3311 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3312 ];
3313 let scalar_b: [u8; 32] = [
3314 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3315 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3316 ];
3317
3318 let scalars = [scalar_a, scalar_b];
3319 let scalars_va = 0x100000000;
3320
3321 let edwards_point_x: [u8; 32] = [
3322 252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3323 53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3324 ];
3325 let edwards_point_y: [u8; 32] = [
3326 10, 111, 8, 236, 97, 189, 124, 69, 89, 176, 222, 39, 199, 253, 111, 11, 248, 186, 128,
3327 90, 120, 128, 248, 210, 232, 183, 93, 104, 111, 150, 7, 241,
3328 ];
3329 let edwards_points = [edwards_point_x, edwards_point_y];
3330 let edwards_points_va = 0x200000000;
3331
3332 let ristretto_point_x: [u8; 32] = [
3333 130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3334 179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3335 ];
3336 let ristretto_point_y: [u8; 32] = [
3337 152, 156, 155, 197, 152, 232, 92, 206, 219, 159, 193, 134, 121, 128, 139, 36, 56, 191,
3338 51, 143, 72, 204, 87, 76, 110, 124, 101, 96, 238, 158, 42, 108,
3339 ];
3340 let ristretto_points = [ristretto_point_x, ristretto_point_y];
3341 let ristretto_points_va = 0x300000000;
3342
3343 let mut result_point: [u8; 32] = [0; 32];
3344 let result_point_va = 0x400000000;
3345
3346 let mut memory_mapping = MemoryMapping::new(
3347 vec![
3348 MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3349 MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3350 MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3351 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3352 ],
3353 &config,
3354 SBPFVersion::V3,
3355 )
3356 .unwrap();
3357
3358 invoke_context.mock_set_remaining(
3359 invoke_context
3360 .get_execution_cost()
3361 .curve25519_edwards_msm_base_cost
3362 + invoke_context
3363 .get_execution_cost()
3364 .curve25519_edwards_msm_incremental_cost
3365 + invoke_context
3366 .get_execution_cost()
3367 .curve25519_ristretto_msm_base_cost
3368 + invoke_context
3369 .get_execution_cost()
3370 .curve25519_ristretto_msm_incremental_cost,
3371 );
3372
3373 let result = SyscallCurveMultiscalarMultiplication::rust(
3374 &mut invoke_context,
3375 CURVE25519_EDWARDS,
3376 scalars_va,
3377 edwards_points_va,
3378 2,
3379 result_point_va,
3380 &mut memory_mapping,
3381 );
3382
3383 assert_eq!(0, result.unwrap());
3384 let expected_product = [
3385 30, 174, 168, 34, 160, 70, 63, 166, 236, 18, 74, 144, 185, 222, 208, 243, 5, 54, 223,
3386 172, 185, 75, 244, 26, 70, 18, 248, 46, 207, 184, 235, 60,
3387 ];
3388 assert_eq!(expected_product, result_point);
3389
3390 let result = SyscallCurveMultiscalarMultiplication::rust(
3391 &mut invoke_context,
3392 CURVE25519_RISTRETTO,
3393 scalars_va,
3394 ristretto_points_va,
3395 2,
3396 result_point_va,
3397 &mut memory_mapping,
3398 );
3399
3400 assert_eq!(0, result.unwrap());
3401 let expected_product = [
3402 78, 120, 86, 111, 152, 64, 146, 84, 14, 236, 77, 147, 237, 190, 251, 241, 136, 167, 21,
3403 94, 84, 118, 92, 140, 120, 81, 30, 246, 173, 140, 195, 86,
3404 ];
3405 assert_eq!(expected_product, result_point);
3406 }
3407
3408 #[test]
3409 fn test_syscall_multiscalar_multiplication_maximum_length_exceeded() {
3410 use atlas_curve25519::curve_syscall_traits::{CURVE25519_EDWARDS, CURVE25519_RISTRETTO};
3411
3412 let config = Config::default();
3413 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3414
3415 let scalar: [u8; 32] = [
3416 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3417 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3418 ];
3419 let scalars = [scalar; 513];
3420 let scalars_va = 0x100000000;
3421
3422 let edwards_point: [u8; 32] = [
3423 252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3424 53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3425 ];
3426 let edwards_points = [edwards_point; 513];
3427 let edwards_points_va = 0x200000000;
3428
3429 let ristretto_point: [u8; 32] = [
3430 130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3431 179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3432 ];
3433 let ristretto_points = [ristretto_point; 513];
3434 let ristretto_points_va = 0x300000000;
3435
3436 let mut result_point: [u8; 32] = [0; 32];
3437 let result_point_va = 0x400000000;
3438
3439 let mut memory_mapping = MemoryMapping::new(
3440 vec![
3441 MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3442 MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3443 MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3444 MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3445 ],
3446 &config,
3447 SBPFVersion::V3,
3448 )
3449 .unwrap();
3450
3451 invoke_context.mock_set_remaining(500_000);
3453 let result = SyscallCurveMultiscalarMultiplication::rust(
3454 &mut invoke_context,
3455 CURVE25519_EDWARDS,
3456 scalars_va,
3457 edwards_points_va,
3458 512, result_point_va,
3460 &mut memory_mapping,
3461 );
3462
3463 assert_eq!(0, result.unwrap());
3464 let expected_product = [
3465 20, 146, 226, 37, 22, 61, 86, 249, 208, 40, 38, 11, 126, 101, 10, 82, 81, 77, 88, 209,
3466 15, 76, 82, 251, 180, 133, 84, 243, 162, 0, 11, 145,
3467 ];
3468 assert_eq!(expected_product, result_point);
3469
3470 invoke_context.mock_set_remaining(500_000);
3471 let result = SyscallCurveMultiscalarMultiplication::rust(
3472 &mut invoke_context,
3473 CURVE25519_EDWARDS,
3474 scalars_va,
3475 edwards_points_va,
3476 513, result_point_va,
3478 &mut memory_mapping,
3479 )
3480 .unwrap_err()
3481 .downcast::<SyscallError>()
3482 .unwrap();
3483
3484 assert_eq!(*result, SyscallError::InvalidLength);
3485
3486 invoke_context.mock_set_remaining(500_000);
3488 let result = SyscallCurveMultiscalarMultiplication::rust(
3489 &mut invoke_context,
3490 CURVE25519_RISTRETTO,
3491 scalars_va,
3492 ristretto_points_va,
3493 512, result_point_va,
3495 &mut memory_mapping,
3496 );
3497
3498 assert_eq!(0, result.unwrap());
3499 let expected_product = [
3500 146, 224, 127, 193, 252, 64, 196, 181, 246, 104, 27, 116, 183, 52, 200, 239, 2, 108,
3501 21, 27, 97, 44, 95, 65, 26, 218, 223, 39, 197, 132, 51, 49,
3502 ];
3503 assert_eq!(expected_product, result_point);
3504
3505 invoke_context.mock_set_remaining(500_000);
3506 let result = SyscallCurveMultiscalarMultiplication::rust(
3507 &mut invoke_context,
3508 CURVE25519_RISTRETTO,
3509 scalars_va,
3510 ristretto_points_va,
3511 513, result_point_va,
3513 &mut memory_mapping,
3514 )
3515 .unwrap_err()
3516 .downcast::<SyscallError>()
3517 .unwrap();
3518
3519 assert_eq!(*result, SyscallError::InvalidLength);
3520 }
3521
3522 fn create_filled_type<T: Default>(zero_init: bool) -> T {
3523 let mut val = T::default();
3524 let p = &mut val as *mut _ as *mut u8;
3525 for i in 0..(size_of::<T>() as isize) {
3526 unsafe {
3527 *p.offset(i) = if zero_init { 0 } else { i as u8 };
3528 }
3529 }
3530 val
3531 }
3532
3533 fn are_bytes_equal<T>(first: &T, second: &T) -> bool {
3534 let p_first = first as *const _ as *const u8;
3535 let p_second = second as *const _ as *const u8;
3536
3537 for i in 0..(size_of::<T>() as isize) {
3538 unsafe {
3539 if *p_first.offset(i) != *p_second.offset(i) {
3540 return false;
3541 }
3542 }
3543 }
3544 true
3545 }
3546
3547 #[test]
3548 #[allow(deprecated)]
3549 fn test_syscall_get_sysvar() {
3550 let config = Config::default();
3551
3552 let mut src_clock = create_filled_type::<Clock>(false);
3553 src_clock.slot = 1;
3554 src_clock.epoch_start_timestamp = 2;
3555 src_clock.epoch = 3;
3556 src_clock.leader_schedule_epoch = 4;
3557 src_clock.unix_timestamp = 5;
3558
3559 let mut src_epochschedule = create_filled_type::<EpochSchedule>(false);
3560 src_epochschedule.slots_per_epoch = 1;
3561 src_epochschedule.leader_schedule_slot_offset = 2;
3562 src_epochschedule.warmup = false;
3563 src_epochschedule.first_normal_epoch = 3;
3564 src_epochschedule.first_normal_slot = 4;
3565
3566 let mut src_fees = create_filled_type::<Fees>(false);
3567 src_fees.fee_calculator = FeeCalculator {
3568 lamports_per_signature: 1,
3569 };
3570
3571 let mut src_rent = create_filled_type::<Rent>(false);
3572 src_rent.lamports_per_byte_year = 1;
3573 src_rent.exemption_threshold = 2.0;
3574 src_rent.burn_percent = 3;
3575
3576 let mut src_rewards = create_filled_type::<EpochRewards>(false);
3577 src_rewards.distribution_starting_block_height = 42;
3578 src_rewards.num_partitions = 2;
3579 src_rewards.parent_blockhash = Hash::new_from_array([3; 32]);
3580 src_rewards.total_points = 4;
3581 src_rewards.total_rewards = 100;
3582 src_rewards.distributed_rewards = 10;
3583 src_rewards.active = true;
3584
3585 let mut src_restart = create_filled_type::<LastRestartSlot>(false);
3586 src_restart.last_restart_slot = 1;
3587
3588 let transaction_accounts = vec![
3589 (
3590 sysvar::clock::id(),
3591 create_account_shared_data_for_test(&src_clock),
3592 ),
3593 (
3594 sysvar::epoch_schedule::id(),
3595 create_account_shared_data_for_test(&src_epochschedule),
3596 ),
3597 (
3598 sysvar::fees::id(),
3599 create_account_shared_data_for_test(&src_fees),
3600 ),
3601 (
3602 sysvar::rent::id(),
3603 create_account_shared_data_for_test(&src_rent),
3604 ),
3605 (
3606 sysvar::epoch_rewards::id(),
3607 create_account_shared_data_for_test(&src_rewards),
3608 ),
3609 (
3610 sysvar::last_restart_slot::id(),
3611 create_account_shared_data_for_test(&src_restart),
3612 ),
3613 ];
3614 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
3615
3616 {
3618 let mut got_clock_obj = Clock::default();
3619 let got_clock_obj_va = 0x100000000;
3620
3621 let mut got_clock_buf = vec![0; Clock::size_of()];
3622 let got_clock_buf_va = 0x200000000;
3623 let clock_id_va = 0x300000000;
3624 let clock_id = Clock::id().to_bytes();
3625
3626 let mut memory_mapping = MemoryMapping::new(
3627 vec![
3628 MemoryRegion::new_writable(bytes_of_mut(&mut got_clock_obj), got_clock_obj_va),
3629 MemoryRegion::new_writable(&mut got_clock_buf, got_clock_buf_va),
3630 MemoryRegion::new_readonly(&clock_id, clock_id_va),
3631 ],
3632 &config,
3633 SBPFVersion::V3,
3634 )
3635 .unwrap();
3636
3637 let result = SyscallGetClockSysvar::rust(
3638 &mut invoke_context,
3639 got_clock_obj_va,
3640 0,
3641 0,
3642 0,
3643 0,
3644 &mut memory_mapping,
3645 );
3646 assert_eq!(result.unwrap(), 0);
3647 assert_eq!(got_clock_obj, src_clock);
3648
3649 let mut clean_clock = create_filled_type::<Clock>(true);
3650 clean_clock.slot = src_clock.slot;
3651 clean_clock.epoch_start_timestamp = src_clock.epoch_start_timestamp;
3652 clean_clock.epoch = src_clock.epoch;
3653 clean_clock.leader_schedule_epoch = src_clock.leader_schedule_epoch;
3654 clean_clock.unix_timestamp = src_clock.unix_timestamp;
3655 assert!(are_bytes_equal(&got_clock_obj, &clean_clock));
3656
3657 let result = SyscallGetSysvar::rust(
3658 &mut invoke_context,
3659 clock_id_va,
3660 got_clock_buf_va,
3661 0,
3662 Clock::size_of() as u64,
3663 0,
3664 &mut memory_mapping,
3665 );
3666 assert_eq!(result.unwrap(), 0);
3667
3668 let clock_from_buf = bincode::deserialize::<Clock>(&got_clock_buf).unwrap();
3669
3670 assert_eq!(clock_from_buf, src_clock);
3671 assert!(are_bytes_equal(&clock_from_buf, &clean_clock));
3672 }
3673
3674 {
3676 let mut got_epochschedule_obj = EpochSchedule::default();
3677 let got_epochschedule_obj_va = 0x100000000;
3678
3679 let mut got_epochschedule_buf = vec![0; EpochSchedule::size_of()];
3680 let got_epochschedule_buf_va = 0x200000000;
3681 let epochschedule_id_va = 0x300000000;
3682 let epochschedule_id = EpochSchedule::id().to_bytes();
3683
3684 let mut memory_mapping = MemoryMapping::new(
3685 vec![
3686 MemoryRegion::new_writable(
3687 bytes_of_mut(&mut got_epochschedule_obj),
3688 got_epochschedule_obj_va,
3689 ),
3690 MemoryRegion::new_writable(
3691 &mut got_epochschedule_buf,
3692 got_epochschedule_buf_va,
3693 ),
3694 MemoryRegion::new_readonly(&epochschedule_id, epochschedule_id_va),
3695 ],
3696 &config,
3697 SBPFVersion::V3,
3698 )
3699 .unwrap();
3700
3701 let result = SyscallGetEpochScheduleSysvar::rust(
3702 &mut invoke_context,
3703 got_epochschedule_obj_va,
3704 0,
3705 0,
3706 0,
3707 0,
3708 &mut memory_mapping,
3709 );
3710 assert_eq!(result.unwrap(), 0);
3711 assert_eq!(got_epochschedule_obj, src_epochschedule);
3712
3713 let mut clean_epochschedule = create_filled_type::<EpochSchedule>(true);
3714 clean_epochschedule.slots_per_epoch = src_epochschedule.slots_per_epoch;
3715 clean_epochschedule.leader_schedule_slot_offset =
3716 src_epochschedule.leader_schedule_slot_offset;
3717 clean_epochschedule.warmup = src_epochschedule.warmup;
3718 clean_epochschedule.first_normal_epoch = src_epochschedule.first_normal_epoch;
3719 clean_epochschedule.first_normal_slot = src_epochschedule.first_normal_slot;
3720 assert!(are_bytes_equal(
3721 &got_epochschedule_obj,
3722 &clean_epochschedule
3723 ));
3724
3725 let result = SyscallGetSysvar::rust(
3726 &mut invoke_context,
3727 epochschedule_id_va,
3728 got_epochschedule_buf_va,
3729 0,
3730 EpochSchedule::size_of() as u64,
3731 0,
3732 &mut memory_mapping,
3733 );
3734 assert_eq!(result.unwrap(), 0);
3735
3736 let epochschedule_from_buf =
3737 bincode::deserialize::<EpochSchedule>(&got_epochschedule_buf).unwrap();
3738
3739 assert_eq!(epochschedule_from_buf, src_epochschedule);
3740
3741 assert!(are_bytes_equal(
3743 &epochschedule_from_buf.clone(),
3744 &clean_epochschedule
3745 ));
3746 }
3747
3748 {
3750 let mut got_fees = Fees::default();
3751 let got_fees_va = 0x100000000;
3752
3753 let mut memory_mapping = MemoryMapping::new(
3754 vec![MemoryRegion::new_writable(
3755 bytes_of_mut(&mut got_fees),
3756 got_fees_va,
3757 )],
3758 &config,
3759 SBPFVersion::V3,
3760 )
3761 .unwrap();
3762
3763 let result = SyscallGetFeesSysvar::rust(
3764 &mut invoke_context,
3765 got_fees_va,
3766 0,
3767 0,
3768 0,
3769 0,
3770 &mut memory_mapping,
3771 );
3772 assert_eq!(result.unwrap(), 0);
3773 assert_eq!(got_fees, src_fees);
3774
3775 let mut clean_fees = create_filled_type::<Fees>(true);
3776 clean_fees.fee_calculator = src_fees.fee_calculator;
3777 assert!(are_bytes_equal(&got_fees, &clean_fees));
3778
3779 }
3781
3782 {
3784 let mut got_rent_obj = create_filled_type::<Rent>(true);
3785 let got_rent_obj_va = 0x100000000;
3786
3787 let mut got_rent_buf = vec![0; Rent::size_of()];
3788 let got_rent_buf_va = 0x200000000;
3789 let rent_id_va = 0x300000000;
3790 let rent_id = Rent::id().to_bytes();
3791
3792 let mut memory_mapping = MemoryMapping::new(
3793 vec![
3794 MemoryRegion::new_writable(bytes_of_mut(&mut got_rent_obj), got_rent_obj_va),
3795 MemoryRegion::new_writable(&mut got_rent_buf, got_rent_buf_va),
3796 MemoryRegion::new_readonly(&rent_id, rent_id_va),
3797 ],
3798 &config,
3799 SBPFVersion::V3,
3800 )
3801 .unwrap();
3802
3803 let result = SyscallGetRentSysvar::rust(
3804 &mut invoke_context,
3805 got_rent_obj_va,
3806 0,
3807 0,
3808 0,
3809 0,
3810 &mut memory_mapping,
3811 );
3812 assert_eq!(result.unwrap(), 0);
3813 assert_eq!(got_rent_obj, src_rent);
3814
3815 let mut clean_rent = create_filled_type::<Rent>(true);
3816 clean_rent.lamports_per_byte_year = src_rent.lamports_per_byte_year;
3817 clean_rent.exemption_threshold = src_rent.exemption_threshold;
3818 clean_rent.burn_percent = src_rent.burn_percent;
3819 assert!(are_bytes_equal(&got_rent_obj, &clean_rent));
3820
3821 let result = SyscallGetSysvar::rust(
3822 &mut invoke_context,
3823 rent_id_va,
3824 got_rent_buf_va,
3825 0,
3826 Rent::size_of() as u64,
3827 0,
3828 &mut memory_mapping,
3829 );
3830 assert_eq!(result.unwrap(), 0);
3831
3832 let rent_from_buf = bincode::deserialize::<Rent>(&got_rent_buf).unwrap();
3833
3834 assert_eq!(rent_from_buf, src_rent);
3835
3836 assert!(are_bytes_equal(&rent_from_buf.clone(), &clean_rent));
3838 }
3839
3840 {
3842 let mut got_rewards_obj = create_filled_type::<EpochRewards>(true);
3843 let got_rewards_obj_va = 0x100000000;
3844
3845 let mut got_rewards_buf = vec![0; EpochRewards::size_of()];
3846 let got_rewards_buf_va = 0x200000000;
3847 let rewards_id_va = 0x300000000;
3848 let rewards_id = EpochRewards::id().to_bytes();
3849
3850 let mut memory_mapping = MemoryMapping::new(
3851 vec![
3852 MemoryRegion::new_writable(
3853 bytes_of_mut(&mut got_rewards_obj),
3854 got_rewards_obj_va,
3855 ),
3856 MemoryRegion::new_writable(&mut got_rewards_buf, got_rewards_buf_va),
3857 MemoryRegion::new_readonly(&rewards_id, rewards_id_va),
3858 ],
3859 &config,
3860 SBPFVersion::V3,
3861 )
3862 .unwrap();
3863
3864 let result = SyscallGetEpochRewardsSysvar::rust(
3865 &mut invoke_context,
3866 got_rewards_obj_va,
3867 0,
3868 0,
3869 0,
3870 0,
3871 &mut memory_mapping,
3872 );
3873 assert_eq!(result.unwrap(), 0);
3874 assert_eq!(got_rewards_obj, src_rewards);
3875
3876 let mut clean_rewards = create_filled_type::<EpochRewards>(true);
3877 clean_rewards.distribution_starting_block_height =
3878 src_rewards.distribution_starting_block_height;
3879 clean_rewards.num_partitions = src_rewards.num_partitions;
3880 clean_rewards.parent_blockhash = src_rewards.parent_blockhash;
3881 clean_rewards.total_points = src_rewards.total_points;
3882 clean_rewards.total_rewards = src_rewards.total_rewards;
3883 clean_rewards.distributed_rewards = src_rewards.distributed_rewards;
3884 clean_rewards.active = src_rewards.active;
3885 assert!(are_bytes_equal(&got_rewards_obj, &clean_rewards));
3886
3887 let result = SyscallGetSysvar::rust(
3888 &mut invoke_context,
3889 rewards_id_va,
3890 got_rewards_buf_va,
3891 0,
3892 EpochRewards::size_of() as u64,
3893 0,
3894 &mut memory_mapping,
3895 );
3896 assert_eq!(result.unwrap(), 0);
3897
3898 let rewards_from_buf = bincode::deserialize::<EpochRewards>(&got_rewards_buf).unwrap();
3899
3900 assert_eq!(rewards_from_buf, src_rewards);
3901
3902 assert!(are_bytes_equal(&rewards_from_buf.clone(), &clean_rewards));
3904 }
3905
3906 {
3908 let mut got_restart_obj = LastRestartSlot::default();
3909 let got_restart_obj_va = 0x100000000;
3910
3911 let mut got_restart_buf = vec![0; LastRestartSlot::size_of()];
3912 let got_restart_buf_va = 0x200000000;
3913 let restart_id_va = 0x300000000;
3914 let restart_id = LastRestartSlot::id().to_bytes();
3915
3916 let mut memory_mapping = MemoryMapping::new(
3917 vec![
3918 MemoryRegion::new_writable(
3919 bytes_of_mut(&mut got_restart_obj),
3920 got_restart_obj_va,
3921 ),
3922 MemoryRegion::new_writable(&mut got_restart_buf, got_restart_buf_va),
3923 MemoryRegion::new_readonly(&restart_id, restart_id_va),
3924 ],
3925 &config,
3926 SBPFVersion::V3,
3927 )
3928 .unwrap();
3929
3930 let result = SyscallGetLastRestartSlotSysvar::rust(
3931 &mut invoke_context,
3932 got_restart_obj_va,
3933 0,
3934 0,
3935 0,
3936 0,
3937 &mut memory_mapping,
3938 );
3939 assert_eq!(result.unwrap(), 0);
3940 assert_eq!(got_restart_obj, src_restart);
3941
3942 let mut clean_restart = create_filled_type::<LastRestartSlot>(true);
3943 clean_restart.last_restart_slot = src_restart.last_restart_slot;
3944 assert!(are_bytes_equal(&got_restart_obj, &clean_restart));
3945
3946 let result = SyscallGetSysvar::rust(
3947 &mut invoke_context,
3948 restart_id_va,
3949 got_restart_buf_va,
3950 0,
3951 LastRestartSlot::size_of() as u64,
3952 0,
3953 &mut memory_mapping,
3954 );
3955 assert_eq!(result.unwrap(), 0);
3956
3957 let restart_from_buf =
3958 bincode::deserialize::<LastRestartSlot>(&got_restart_buf).unwrap();
3959
3960 assert_eq!(restart_from_buf, src_restart);
3961 assert!(are_bytes_equal(&restart_from_buf, &clean_restart));
3962 }
3963 }
3964
3965 #[test_case(false; "partial")]
3966 #[test_case(true; "full")]
3967 fn test_syscall_get_stake_history(filled: bool) {
3968 let config = Config::default();
3969
3970 let mut src_history = StakeHistory::default();
3971
3972 let epochs = if filled {
3973 stake_history::MAX_ENTRIES + 1
3974 } else {
3975 stake_history::MAX_ENTRIES / 2
3976 } as u64;
3977
3978 for epoch in 1..epochs {
3979 src_history.add(
3980 epoch,
3981 StakeHistoryEntry {
3982 effective: epoch * 2,
3983 activating: epoch * 3,
3984 deactivating: epoch * 5,
3985 },
3986 );
3987 }
3988
3989 let src_history = src_history;
3990
3991 let mut src_history_buf = vec![0; StakeHistory::size_of()];
3992 bincode::serialize_into(&mut src_history_buf, &src_history).unwrap();
3993
3994 let transaction_accounts = vec![(
3995 sysvar::stake_history::id(),
3996 create_account_shared_data_for_test(&src_history),
3997 )];
3998 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
3999
4000 {
4001 let mut got_history_buf = vec![0; StakeHistory::size_of()];
4002 let got_history_buf_va = 0x100000000;
4003 let history_id_va = 0x200000000;
4004 let history_id = StakeHistory::id().to_bytes();
4005
4006 let mut memory_mapping = MemoryMapping::new(
4007 vec![
4008 MemoryRegion::new_writable(&mut got_history_buf, got_history_buf_va),
4009 MemoryRegion::new_readonly(&history_id, history_id_va),
4010 ],
4011 &config,
4012 SBPFVersion::V3,
4013 )
4014 .unwrap();
4015
4016 let result = SyscallGetSysvar::rust(
4017 &mut invoke_context,
4018 history_id_va,
4019 got_history_buf_va,
4020 0,
4021 StakeHistory::size_of() as u64,
4022 0,
4023 &mut memory_mapping,
4024 );
4025 assert_eq!(result.unwrap(), 0);
4026
4027 let history_from_buf = bincode::deserialize::<StakeHistory>(&got_history_buf).unwrap();
4028 assert_eq!(history_from_buf, src_history);
4029 }
4030 }
4031
4032 #[test_case(false; "partial")]
4033 #[test_case(true; "full")]
4034 fn test_syscall_get_slot_hashes(filled: bool) {
4035 let config = Config::default();
4036
4037 let mut src_hashes = SlotHashes::default();
4038
4039 let slots = if filled {
4040 slot_hashes::MAX_ENTRIES + 1
4041 } else {
4042 slot_hashes::MAX_ENTRIES / 2
4043 } as u64;
4044
4045 for slot in 1..slots {
4046 src_hashes.add(slot, hashv(&[&slot.to_le_bytes()]));
4047 }
4048
4049 let src_hashes = src_hashes;
4050
4051 let mut src_hashes_buf = vec![0; SlotHashes::size_of()];
4052 bincode::serialize_into(&mut src_hashes_buf, &src_hashes).unwrap();
4053
4054 let transaction_accounts = vec![(
4055 sysvar::slot_hashes::id(),
4056 create_account_shared_data_for_test(&src_hashes),
4057 )];
4058 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4059
4060 {
4061 let mut got_hashes_buf = vec![0; SlotHashes::size_of()];
4062 let got_hashes_buf_va = 0x100000000;
4063 let hashes_id_va = 0x200000000;
4064 let hashes_id = SlotHashes::id().to_bytes();
4065
4066 let mut memory_mapping = MemoryMapping::new(
4067 vec![
4068 MemoryRegion::new_writable(&mut got_hashes_buf, got_hashes_buf_va),
4069 MemoryRegion::new_readonly(&hashes_id, hashes_id_va),
4070 ],
4071 &config,
4072 SBPFVersion::V3,
4073 )
4074 .unwrap();
4075
4076 let result = SyscallGetSysvar::rust(
4077 &mut invoke_context,
4078 hashes_id_va,
4079 got_hashes_buf_va,
4080 0,
4081 SlotHashes::size_of() as u64,
4082 0,
4083 &mut memory_mapping,
4084 );
4085 assert_eq!(result.unwrap(), 0);
4086
4087 let hashes_from_buf = bincode::deserialize::<SlotHashes>(&got_hashes_buf).unwrap();
4088 assert_eq!(hashes_from_buf, src_hashes);
4089 }
4090 }
4091
4092 #[test]
4093 fn test_syscall_get_sysvar_errors() {
4094 let config = Config::default();
4095
4096 let mut src_clock = create_filled_type::<Clock>(false);
4097 src_clock.slot = 1;
4098 src_clock.epoch_start_timestamp = 2;
4099 src_clock.epoch = 3;
4100 src_clock.leader_schedule_epoch = 4;
4101 src_clock.unix_timestamp = 5;
4102
4103 let clock_id_va = 0x100000000;
4104 let clock_id = Clock::id().to_bytes();
4105
4106 let mut got_clock_buf_rw = vec![0; Clock::size_of()];
4107 let got_clock_buf_rw_va = 0x200000000;
4108
4109 let got_clock_buf_ro = vec![0; Clock::size_of()];
4110 let got_clock_buf_ro_va = 0x300000000;
4111
4112 let mut memory_mapping = MemoryMapping::new(
4113 vec![
4114 MemoryRegion::new_readonly(&clock_id, clock_id_va),
4115 MemoryRegion::new_writable(&mut got_clock_buf_rw, got_clock_buf_rw_va),
4116 MemoryRegion::new_readonly(&got_clock_buf_ro, got_clock_buf_ro_va),
4117 ],
4118 &config,
4119 SBPFVersion::V3,
4120 )
4121 .unwrap();
4122
4123 let access_violation_err =
4124 std::mem::discriminant(&EbpfError::AccessViolation(AccessType::Load, 0, 0, ""));
4125
4126 let got_clock_empty = vec![0; Clock::size_of()];
4127
4128 {
4129 with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4131
4132 let e = SyscallGetSysvar::rust(
4134 &mut invoke_context,
4135 clock_id_va + 1,
4136 got_clock_buf_rw_va,
4137 0,
4138 Clock::size_of() as u64,
4139 0,
4140 &mut memory_mapping,
4141 )
4142 .unwrap_err();
4143
4144 assert_eq!(
4145 std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4146 access_violation_err,
4147 );
4148 assert_eq!(got_clock_buf_rw, got_clock_empty);
4149
4150 let e = SyscallGetSysvar::rust(
4152 &mut invoke_context,
4153 clock_id_va,
4154 got_clock_buf_rw_va + 1,
4155 0,
4156 Clock::size_of() as u64,
4157 0,
4158 &mut memory_mapping,
4159 )
4160 .unwrap_err();
4161
4162 assert_eq!(
4163 std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4164 access_violation_err,
4165 );
4166 assert_eq!(got_clock_buf_rw, got_clock_empty);
4167
4168 let e = SyscallGetSysvar::rust(
4169 &mut invoke_context,
4170 clock_id_va,
4171 got_clock_buf_ro_va,
4172 0,
4173 Clock::size_of() as u64,
4174 0,
4175 &mut memory_mapping,
4176 )
4177 .unwrap_err();
4178
4179 assert_eq!(
4180 std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4181 access_violation_err,
4182 );
4183 assert_eq!(got_clock_buf_rw, got_clock_empty);
4184
4185 let e = SyscallGetSysvar::rust(
4187 &mut invoke_context,
4188 clock_id_va,
4189 got_clock_buf_rw_va,
4190 u64::MAX - Clock::size_of() as u64 / 2,
4191 Clock::size_of() as u64,
4192 0,
4193 &mut memory_mapping,
4194 )
4195 .unwrap_err();
4196
4197 assert_eq!(
4198 *e.downcast_ref::<InstructionError>().unwrap(),
4199 InstructionError::ArithmeticOverflow,
4200 );
4201 assert_eq!(got_clock_buf_rw, got_clock_empty);
4202
4203 let result = SyscallGetSysvar::rust(
4208 &mut invoke_context,
4209 clock_id_va,
4210 got_clock_buf_rw_va,
4211 0,
4212 Clock::size_of() as u64,
4213 0,
4214 &mut memory_mapping,
4215 )
4216 .unwrap();
4217
4218 assert_eq!(result, 2);
4219 assert_eq!(got_clock_buf_rw, got_clock_empty);
4220 }
4221
4222 {
4223 let transaction_accounts = vec![(
4224 sysvar::clock::id(),
4225 create_account_shared_data_for_test(&src_clock),
4226 )];
4227 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4228
4229 let result = SyscallGetSysvar::rust(
4231 &mut invoke_context,
4232 clock_id_va,
4233 got_clock_buf_rw_va,
4234 1,
4235 Clock::size_of() as u64,
4236 0,
4237 &mut memory_mapping,
4238 )
4239 .unwrap();
4240
4241 assert_eq!(result, 1);
4242 assert_eq!(got_clock_buf_rw, got_clock_empty);
4243
4244 SyscallGetSysvar::rust(
4246 &mut invoke_context,
4247 clock_id_va,
4248 got_clock_buf_rw_va,
4249 0,
4250 Clock::size_of() as u64,
4251 0,
4252 &mut memory_mapping,
4253 )
4254 .unwrap();
4255
4256 let clock_from_buf = bincode::deserialize::<Clock>(&got_clock_buf_rw).unwrap();
4257
4258 assert_eq!(clock_from_buf, src_clock);
4259 }
4260 }
4261
4262 type BuiltinFunctionRustInterface<'a> = fn(
4263 &mut InvokeContext<'a>,
4264 u64,
4265 u64,
4266 u64,
4267 u64,
4268 u64,
4269 &mut MemoryMapping,
4270 ) -> Result<u64, Box<dyn std::error::Error>>;
4271
4272 fn call_program_address_common<'a, 'b: 'a>(
4273 invoke_context: &'a mut InvokeContext<'b>,
4274 seeds: &[&[u8]],
4275 program_id: &Pubkey,
4276 overlap_outputs: bool,
4277 syscall: BuiltinFunctionRustInterface<'b>,
4278 ) -> Result<(Pubkey, u8), Error> {
4279 const SEEDS_VA: u64 = 0x100000000;
4280 const PROGRAM_ID_VA: u64 = 0x200000000;
4281 const ADDRESS_VA: u64 = 0x300000000;
4282 const BUMP_SEED_VA: u64 = 0x400000000;
4283 const SEED_VA: u64 = 0x500000000;
4284
4285 let config = Config::default();
4286 let mut address = Pubkey::default();
4287 let mut bump_seed = 0;
4288 let mut regions = vec![
4289 MemoryRegion::new_readonly(bytes_of(program_id), PROGRAM_ID_VA),
4290 MemoryRegion::new_writable(bytes_of_mut(&mut address), ADDRESS_VA),
4291 MemoryRegion::new_writable(bytes_of_mut(&mut bump_seed), BUMP_SEED_VA),
4292 ];
4293
4294 let mut mock_slices = Vec::with_capacity(seeds.len());
4295 for (i, seed) in seeds.iter().enumerate() {
4296 let vm_addr = SEED_VA.saturating_add((i as u64).saturating_mul(0x100000000));
4297 let mock_slice = MockSlice {
4298 vm_addr,
4299 len: seed.len(),
4300 };
4301 mock_slices.push(mock_slice);
4302 regions.push(MemoryRegion::new_readonly(bytes_of_slice(seed), vm_addr));
4303 }
4304 regions.push(MemoryRegion::new_readonly(
4305 bytes_of_slice(&mock_slices),
4306 SEEDS_VA,
4307 ));
4308 let mut memory_mapping = MemoryMapping::new(regions, &config, SBPFVersion::V3).unwrap();
4309
4310 let result = syscall(
4311 invoke_context,
4312 SEEDS_VA,
4313 seeds.len() as u64,
4314 PROGRAM_ID_VA,
4315 ADDRESS_VA,
4316 if overlap_outputs {
4317 ADDRESS_VA
4318 } else {
4319 BUMP_SEED_VA
4320 },
4321 &mut memory_mapping,
4322 );
4323 result.map(|_| (address, bump_seed))
4324 }
4325
4326 fn create_program_address(
4327 invoke_context: &mut InvokeContext,
4328 seeds: &[&[u8]],
4329 address: &Pubkey,
4330 ) -> Result<Pubkey, Error> {
4331 let (address, _) = call_program_address_common(
4332 invoke_context,
4333 seeds,
4334 address,
4335 false,
4336 SyscallCreateProgramAddress::rust,
4337 )?;
4338 Ok(address)
4339 }
4340
4341 fn try_find_program_address(
4342 invoke_context: &mut InvokeContext,
4343 seeds: &[&[u8]],
4344 address: &Pubkey,
4345 ) -> Result<(Pubkey, u8), Error> {
4346 call_program_address_common(
4347 invoke_context,
4348 seeds,
4349 address,
4350 false,
4351 SyscallTryFindProgramAddress::rust,
4352 )
4353 }
4354
4355 #[test]
4356 fn test_set_and_get_return_data() {
4357 const SRC_VA: u64 = 0x100000000;
4358 const DST_VA: u64 = 0x200000000;
4359 const PROGRAM_ID_VA: u64 = 0x300000000;
4360 let data = vec![42; 24];
4361 let mut data_buffer = vec![0; 16];
4362 let mut id_buffer = vec![0; 32];
4363
4364 let config = Config::default();
4365 let mut memory_mapping = MemoryMapping::new(
4366 vec![
4367 MemoryRegion::new_readonly(&data, SRC_VA),
4368 MemoryRegion::new_writable(&mut data_buffer, DST_VA),
4369 MemoryRegion::new_writable(&mut id_buffer, PROGRAM_ID_VA),
4370 ],
4371 &config,
4372 SBPFVersion::V3,
4373 )
4374 .unwrap();
4375
4376 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4377
4378 let result = SyscallSetReturnData::rust(
4379 &mut invoke_context,
4380 SRC_VA,
4381 data.len() as u64,
4382 0,
4383 0,
4384 0,
4385 &mut memory_mapping,
4386 );
4387 assert_eq!(result.unwrap(), 0);
4388
4389 let result = SyscallGetReturnData::rust(
4390 &mut invoke_context,
4391 DST_VA,
4392 data_buffer.len() as u64,
4393 PROGRAM_ID_VA,
4394 0,
4395 0,
4396 &mut memory_mapping,
4397 );
4398 assert_eq!(result.unwrap() as usize, data.len());
4399 assert_eq!(data.get(0..data_buffer.len()).unwrap(), data_buffer);
4400 assert_eq!(id_buffer, program_id.to_bytes());
4401
4402 let result = SyscallGetReturnData::rust(
4403 &mut invoke_context,
4404 PROGRAM_ID_VA,
4405 data_buffer.len() as u64,
4406 PROGRAM_ID_VA,
4407 0,
4408 0,
4409 &mut memory_mapping,
4410 );
4411 assert_matches!(
4412 result,
4413 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4414 );
4415 }
4416
4417 #[test]
4418 fn test_syscall_sol_get_processed_sibling_instruction() {
4419 let transaction_accounts = (0..9)
4420 .map(|_| {
4421 (
4422 Pubkey::new_unique(),
4423 AccountSharedData::new(0, 0, &bpf_loader::id()),
4424 )
4425 })
4426 .collect::<Vec<_>>();
4427 let instruction_trace = [1, 2, 3, 2, 2, 3, 4, 3];
4428 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4429 for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
4430 while stack_height
4431 <= invoke_context
4432 .transaction_context
4433 .get_instruction_stack_height()
4434 {
4435 invoke_context.transaction_context.pop().unwrap();
4436 }
4437 if stack_height
4438 > invoke_context
4439 .transaction_context
4440 .get_instruction_stack_height()
4441 {
4442 let instruction_accounts = vec![InstructionAccount::new(
4443 index_in_trace.saturating_add(1) as IndexOfAccount,
4444 false,
4445 false,
4446 )];
4447 invoke_context
4448 .transaction_context
4449 .configure_next_instruction_for_tests(
4450 0,
4451 instruction_accounts,
4452 &[index_in_trace as u8],
4453 )
4454 .unwrap();
4455 invoke_context.transaction_context.push().unwrap();
4456 }
4457 }
4458
4459 let syscall_base_cost = invoke_context.get_execution_cost().syscall_base_cost;
4460
4461 const VM_BASE_ADDRESS: u64 = 0x100000000;
4462 const META_OFFSET: usize = 0;
4463 const PROGRAM_ID_OFFSET: usize =
4464 META_OFFSET + std::mem::size_of::<ProcessedSiblingInstruction>();
4465 const DATA_OFFSET: usize = PROGRAM_ID_OFFSET + std::mem::size_of::<Pubkey>();
4466 const ACCOUNTS_OFFSET: usize = DATA_OFFSET + 0x100;
4467 const END_OFFSET: usize = ACCOUNTS_OFFSET + std::mem::size_of::<AccountInfo>() * 4;
4468 let mut memory = [0u8; END_OFFSET];
4469 let config = Config::default();
4470 let mut memory_mapping = MemoryMapping::new(
4471 vec![MemoryRegion::new_writable(&mut memory, VM_BASE_ADDRESS)],
4472 &config,
4473 SBPFVersion::V3,
4474 )
4475 .unwrap();
4476 let processed_sibling_instruction =
4477 unsafe { &mut *memory.as_mut_ptr().cast::<ProcessedSiblingInstruction>() };
4478 processed_sibling_instruction.data_len = 1;
4479 processed_sibling_instruction.accounts_len = 1;
4480
4481 invoke_context.mock_set_remaining(syscall_base_cost);
4482 let result = SyscallGetProcessedSiblingInstruction::rust(
4483 &mut invoke_context,
4484 0,
4485 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4486 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4487 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4488 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4489 &mut memory_mapping,
4490 );
4491 assert_eq!(result.unwrap(), 1);
4492 {
4493 let program_id = translate_type::<Pubkey>(
4494 &memory_mapping,
4495 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4496 true,
4497 )
4498 .unwrap();
4499 let data = translate_slice::<u8>(
4500 &memory_mapping,
4501 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4502 processed_sibling_instruction.data_len,
4503 true,
4504 )
4505 .unwrap();
4506 let accounts = translate_slice::<AccountMeta>(
4507 &memory_mapping,
4508 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4509 processed_sibling_instruction.accounts_len,
4510 true,
4511 )
4512 .unwrap();
4513 let transaction_context = &invoke_context.transaction_context;
4514 assert_eq!(processed_sibling_instruction.data_len, 1);
4515 assert_eq!(processed_sibling_instruction.accounts_len, 1);
4516 assert_eq!(
4517 program_id,
4518 transaction_context.get_key_of_account_at_index(0).unwrap(),
4519 );
4520 assert_eq!(data, &[5]);
4521 assert_eq!(
4522 accounts,
4523 &[AccountMeta {
4524 pubkey: *transaction_context.get_key_of_account_at_index(6).unwrap(),
4525 is_signer: false,
4526 is_writable: false
4527 }]
4528 );
4529 }
4530
4531 invoke_context.mock_set_remaining(syscall_base_cost);
4532 let result = SyscallGetProcessedSiblingInstruction::rust(
4533 &mut invoke_context,
4534 1,
4535 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4536 VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4537 VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4538 VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4539 &mut memory_mapping,
4540 );
4541 assert_eq!(result.unwrap(), 0);
4542
4543 invoke_context.mock_set_remaining(syscall_base_cost);
4544 let result = SyscallGetProcessedSiblingInstruction::rust(
4545 &mut invoke_context,
4546 0,
4547 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4548 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4549 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4550 VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4551 &mut memory_mapping,
4552 );
4553 assert_matches!(
4554 result,
4555 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4556 );
4557 }
4558
4559 #[test]
4560 fn test_create_program_address() {
4561 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4564 let address = bpf_loader_upgradeable::id();
4565
4566 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
4567 assert_matches!(
4568 create_program_address(&mut invoke_context, &[exceeded_seed], &address),
4569 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4570 );
4571 assert_matches!(
4572 create_program_address(
4573 &mut invoke_context,
4574 &[b"short_seed", exceeded_seed],
4575 &address,
4576 ),
4577 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4578 );
4579 let max_seed = &[0; MAX_SEED_LEN];
4580 assert!(create_program_address(&mut invoke_context, &[max_seed], &address).is_ok());
4581 let exceeded_seeds: &[&[u8]] = &[
4582 &[1],
4583 &[2],
4584 &[3],
4585 &[4],
4586 &[5],
4587 &[6],
4588 &[7],
4589 &[8],
4590 &[9],
4591 &[10],
4592 &[11],
4593 &[12],
4594 &[13],
4595 &[14],
4596 &[15],
4597 &[16],
4598 ];
4599 assert!(create_program_address(&mut invoke_context, exceeded_seeds, &address).is_ok());
4600 let max_seeds: &[&[u8]] = &[
4601 &[1],
4602 &[2],
4603 &[3],
4604 &[4],
4605 &[5],
4606 &[6],
4607 &[7],
4608 &[8],
4609 &[9],
4610 &[10],
4611 &[11],
4612 &[12],
4613 &[13],
4614 &[14],
4615 &[15],
4616 &[16],
4617 &[17],
4618 ];
4619 assert_matches!(
4620 create_program_address(&mut invoke_context, max_seeds, &address),
4621 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4622 );
4623 assert_eq!(
4624 create_program_address(&mut invoke_context, &[b"", &[1]], &address).unwrap(),
4625 "BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
4626 .parse()
4627 .unwrap(),
4628 );
4629 assert_eq!(
4630 create_program_address(&mut invoke_context, &["☉".as_ref(), &[0]], &address).unwrap(),
4631 "13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
4632 .parse()
4633 .unwrap(),
4634 );
4635 assert_eq!(
4636 create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
4637 .unwrap(),
4638 "2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
4639 .parse()
4640 .unwrap(),
4641 );
4642 let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
4643 assert_eq!(
4644 create_program_address(&mut invoke_context, &[public_key.as_ref(), &[1]], &address)
4645 .unwrap(),
4646 "976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
4647 .parse()
4648 .unwrap(),
4649 );
4650 assert_ne!(
4651 create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
4652 .unwrap(),
4653 create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(),
4654 );
4655 invoke_context.mock_set_remaining(0);
4656 assert_matches!(
4657 create_program_address(&mut invoke_context, &[b"", &[1]], &address),
4658 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
4659 );
4660 }
4661
4662 #[test]
4663 fn test_find_program_address() {
4664 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4665 let cost = invoke_context
4666 .get_execution_cost()
4667 .create_program_address_units;
4668 let address = bpf_loader_upgradeable::id();
4669 let max_tries = 256; for _ in 0..1_000 {
4672 let address = Pubkey::new_unique();
4673 invoke_context.mock_set_remaining(cost * max_tries);
4674 let (found_address, bump_seed) =
4675 try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address)
4676 .unwrap();
4677 assert_eq!(
4678 found_address,
4679 create_program_address(
4680 &mut invoke_context,
4681 &[b"Lil'", b"Bits", &[bump_seed]],
4682 &address,
4683 )
4684 .unwrap()
4685 );
4686 }
4687
4688 let seeds: &[&[u8]] = &[b""];
4689 invoke_context.mock_set_remaining(cost * max_tries);
4690 let (_, bump_seed) =
4691 try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
4692 invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64));
4693 try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
4694 invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
4695 assert_matches!(
4696 try_find_program_address(&mut invoke_context, seeds, &address),
4697 Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
4698 );
4699
4700 let exceeded_seed = &[127; MAX_SEED_LEN + 1];
4701 invoke_context.mock_set_remaining(cost * (max_tries - 1));
4702 assert_matches!(
4703 try_find_program_address(&mut invoke_context, &[exceeded_seed], &address),
4704 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4705 );
4706 let exceeded_seeds: &[&[u8]] = &[
4707 &[1],
4708 &[2],
4709 &[3],
4710 &[4],
4711 &[5],
4712 &[6],
4713 &[7],
4714 &[8],
4715 &[9],
4716 &[10],
4717 &[11],
4718 &[12],
4719 &[13],
4720 &[14],
4721 &[15],
4722 &[16],
4723 &[17],
4724 ];
4725 invoke_context.mock_set_remaining(cost * (max_tries - 1));
4726 assert_matches!(
4727 try_find_program_address(&mut invoke_context, exceeded_seeds, &address),
4728 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4729 );
4730
4731 assert_matches!(
4732 call_program_address_common(
4733 &mut invoke_context,
4734 seeds,
4735 &address,
4736 true,
4737 SyscallTryFindProgramAddress::rust,
4738 ),
4739 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4740 );
4741 }
4742
4743 #[test]
4744 fn test_syscall_big_mod_exp() {
4745 let config = Config::default();
4746 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4747
4748 const VADDR_PARAMS: u64 = 0x100000000;
4749 const MAX_LEN: u64 = 512;
4750 const INV_LEN: u64 = MAX_LEN + 1;
4751 let data: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4752 const VADDR_DATA: u64 = 0x200000000;
4753
4754 let mut data_out: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4755 const VADDR_OUT: u64 = 0x300000000;
4756
4757 {
4759 let params_max_len = BigModExpParams {
4760 base: VADDR_DATA as *const u8,
4761 base_len: MAX_LEN,
4762 exponent: VADDR_DATA as *const u8,
4763 exponent_len: MAX_LEN,
4764 modulus: VADDR_DATA as *const u8,
4765 modulus_len: MAX_LEN,
4766 };
4767
4768 let mut memory_mapping = MemoryMapping::new(
4769 vec![
4770 MemoryRegion::new_readonly(bytes_of(¶ms_max_len), VADDR_PARAMS),
4771 MemoryRegion::new_readonly(&data, VADDR_DATA),
4772 MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4773 ],
4774 &config,
4775 SBPFVersion::V3,
4776 )
4777 .unwrap();
4778
4779 let budget = invoke_context.get_execution_cost();
4780 invoke_context.mock_set_remaining(
4781 budget.syscall_base_cost
4782 + (MAX_LEN * MAX_LEN) / budget.big_modular_exponentiation_cost_divisor
4783 + budget.big_modular_exponentiation_base_cost,
4784 );
4785
4786 let result = SyscallBigModExp::rust(
4787 &mut invoke_context,
4788 VADDR_PARAMS,
4789 VADDR_OUT,
4790 0,
4791 0,
4792 0,
4793 &mut memory_mapping,
4794 );
4795
4796 assert_eq!(result.unwrap(), 0);
4797 }
4798
4799 {
4801 let params_inv_len = BigModExpParams {
4802 base: VADDR_DATA as *const u8,
4803 base_len: INV_LEN,
4804 exponent: VADDR_DATA as *const u8,
4805 exponent_len: INV_LEN,
4806 modulus: VADDR_DATA as *const u8,
4807 modulus_len: INV_LEN,
4808 };
4809
4810 let mut memory_mapping = MemoryMapping::new(
4811 vec![
4812 MemoryRegion::new_readonly(bytes_of(¶ms_inv_len), VADDR_PARAMS),
4813 MemoryRegion::new_readonly(&data, VADDR_DATA),
4814 MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4815 ],
4816 &config,
4817 SBPFVersion::V3,
4818 )
4819 .unwrap();
4820
4821 let budget = invoke_context.get_execution_cost();
4822 invoke_context.mock_set_remaining(
4823 budget.syscall_base_cost
4824 + (INV_LEN * INV_LEN) / budget.big_modular_exponentiation_cost_divisor
4825 + budget.big_modular_exponentiation_base_cost,
4826 );
4827
4828 let result = SyscallBigModExp::rust(
4829 &mut invoke_context,
4830 VADDR_PARAMS,
4831 VADDR_OUT,
4832 0,
4833 0,
4834 0,
4835 &mut memory_mapping,
4836 );
4837
4838 assert_matches!(
4839 result,
4840 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::InvalidLength
4841 );
4842 }
4843 }
4844
4845 #[test]
4846 fn test_syscall_get_epoch_stake_total_stake() {
4847 let config = Config::default();
4848 let compute_cost = SVMTransactionExecutionCost::default();
4849 let mut compute_budget = SVMTransactionExecutionBudget::default();
4850 let sysvar_cache = Arc::<SysvarCache>::default();
4851
4852 const EXPECTED_TOTAL_STAKE: u64 = 200_000_000_000_000;
4853
4854 struct MockCallback {}
4855 impl InvokeContextCallback for MockCallback {
4856 fn get_epoch_stake(&self) -> u64 {
4857 EXPECTED_TOTAL_STAKE
4858 }
4859 }
4861
4862 let expected_cus = compute_cost.syscall_base_cost;
4865
4866 compute_budget.compute_unit_limit = expected_cus;
4869
4870 with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4871 let feature_set = SVMFeatureSet::default();
4872 invoke_context.environment_config = EnvironmentConfig::new(
4873 Hash::default(),
4874 0,
4875 &MockCallback {},
4876 &feature_set,
4877 &sysvar_cache,
4878 );
4879
4880 let null_pointer_var = std::ptr::null::<Pubkey>() as u64;
4881
4882 let mut memory_mapping = MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap();
4883
4884 let result = SyscallGetEpochStake::rust(
4885 &mut invoke_context,
4886 null_pointer_var,
4887 0,
4888 0,
4889 0,
4890 0,
4891 &mut memory_mapping,
4892 )
4893 .unwrap();
4894
4895 assert_eq!(result, EXPECTED_TOTAL_STAKE);
4896 }
4897
4898 #[test]
4899 fn test_syscall_get_epoch_stake_vote_account_stake() {
4900 let config = Config::default();
4901 let mut compute_budget = SVMTransactionExecutionBudget::default();
4902 let compute_cost = SVMTransactionExecutionCost::default();
4903 let sysvar_cache = Arc::<SysvarCache>::default();
4904
4905 const TARGET_VOTE_ADDRESS: Pubkey = Pubkey::new_from_array([2; 32]);
4906 const EXPECTED_EPOCH_STAKE: u64 = 55_000_000_000;
4907
4908 struct MockCallback {}
4909 impl InvokeContextCallback for MockCallback {
4910 fn get_epoch_stake_for_vote_account(&self, vote_address: &Pubkey) -> u64 {
4912 if *vote_address == TARGET_VOTE_ADDRESS {
4913 EXPECTED_EPOCH_STAKE
4914 } else {
4915 0
4916 }
4917 }
4918 }
4919
4920 let expected_cus = compute_cost.syscall_base_cost
4925 + (PUBKEY_BYTES as u64) / compute_cost.cpi_bytes_per_unit
4926 + compute_cost.mem_op_base_cost;
4927
4928 compute_budget.compute_unit_limit = expected_cus;
4931
4932 with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4933 let feature_set = SVMFeatureSet::default();
4934 invoke_context.environment_config = EnvironmentConfig::new(
4935 Hash::default(),
4936 0,
4937 &MockCallback {},
4938 &feature_set,
4939 &sysvar_cache,
4940 );
4941
4942 {
4943 let vote_address_var = 0x100000000;
4946
4947 let mut memory_mapping = MemoryMapping::new(
4948 vec![
4949 MemoryRegion::new_readonly(&[2; 31], vote_address_var),
4951 ],
4952 &config,
4953 SBPFVersion::V3,
4954 )
4955 .unwrap();
4956
4957 let result = SyscallGetEpochStake::rust(
4958 &mut invoke_context,
4959 vote_address_var,
4960 0,
4961 0,
4962 0,
4963 0,
4964 &mut memory_mapping,
4965 );
4966
4967 assert_access_violation!(result, vote_address_var, 32);
4968 }
4969
4970 invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);
4971 {
4972 let vote_address_var = 0x100000000;
4976
4977 let mut memory_mapping = MemoryMapping::new(
4978 vec![MemoryRegion::new_readonly(
4979 bytes_of(&TARGET_VOTE_ADDRESS),
4980 vote_address_var,
4981 )],
4982 &config,
4983 SBPFVersion::V3,
4984 )
4985 .unwrap();
4986
4987 let result = SyscallGetEpochStake::rust(
4988 &mut invoke_context,
4989 vote_address_var,
4990 0,
4991 0,
4992 0,
4993 0,
4994 &mut memory_mapping,
4995 )
4996 .unwrap();
4997
4998 assert_eq!(result, EXPECTED_EPOCH_STAKE);
4999 }
5000
5001 invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);
5002 {
5003 let vote_address_var = 0x100000000;
5007 let not_a_vote_address = Pubkey::new_unique(); let mut memory_mapping = MemoryMapping::new(
5010 vec![MemoryRegion::new_readonly(
5011 bytes_of(¬_a_vote_address),
5012 vote_address_var,
5013 )],
5014 &config,
5015 SBPFVersion::V3,
5016 )
5017 .unwrap();
5018
5019 let result = SyscallGetEpochStake::rust(
5020 &mut invoke_context,
5021 vote_address_var,
5022 0,
5023 0,
5024 0,
5025 0,
5026 &mut memory_mapping,
5027 )
5028 .unwrap();
5029
5030 assert_eq!(result, 0); }
5032 }
5033
5034 #[test]
5035 fn test_check_type_assumptions() {
5036 check_type_assumptions();
5037 }
5038
5039 fn bytes_of<T>(val: &T) -> &[u8] {
5040 let size = mem::size_of::<T>();
5041 unsafe { slice::from_raw_parts(std::slice::from_ref(val).as_ptr().cast(), size) }
5042 }
5043
5044 fn bytes_of_mut<T>(val: &mut T) -> &mut [u8] {
5045 let size = mem::size_of::<T>();
5046 unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) }
5047 }
5048
5049 fn bytes_of_slice<T>(val: &[T]) -> &[u8] {
5050 let size = val.len().wrapping_mul(mem::size_of::<T>());
5051 unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) }
5052 }
5053
5054 fn bytes_of_slice_mut<T>(val: &mut [T]) -> &mut [u8] {
5055 let size = val.len().wrapping_mul(mem::size_of::<T>());
5056 unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) }
5057 }
5058
5059 #[test]
5060 fn test_address_is_aligned() {
5061 for address in 0..std::mem::size_of::<u64>() {
5062 assert_eq!(address_is_aligned::<u64>(address as u64), address == 0);
5063 }
5064 }
5065
5066 #[test_case(0x100000004, 0x100000004, &[0x00, 0x00, 0x00, 0x00])] #[test_case(0x100000003, 0x100000004, &[0xFF, 0xFF, 0xFF, 0xFF])] #[test_case(0x100000005, 0x100000004, &[0x01, 0x00, 0x00, 0x00])] #[test_case(0x100000004, 0x200000004, &[0x00, 0x00, 0x00, 0x00])] #[test_case(0x100000003, 0x200000004, &[0xFF, 0xFF, 0xFF, 0xFF])] #[test_case(0x100000005, 0x200000004, &[0x01, 0x00, 0x00, 0x00])] fn test_memcmp_success(src_a: u64, src_b: u64, expected_result: &[u8; 4]) {
5073 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5074 let mem = (0..12).collect::<Vec<u8>>();
5075 let mut result_mem = vec![0; 4];
5076 let config = Config::default();
5077 let mut memory_mapping = MemoryMapping::new(
5078 vec![
5079 MemoryRegion::new_readonly(&mem, 0x100000000),
5080 MemoryRegion::new_readonly(&mem, 0x200000000),
5081 MemoryRegion::new_writable(&mut result_mem, 0x300000000),
5082 ],
5083 &config,
5084 SBPFVersion::V3,
5085 )
5086 .unwrap();
5087
5088 let result = SyscallMemcmp::rust(
5089 &mut invoke_context,
5090 src_a,
5091 src_b,
5092 4,
5093 0x300000000,
5094 0,
5095 &mut memory_mapping,
5096 );
5097 result.unwrap();
5098 assert_eq!(result_mem, expected_result);
5099 }
5100
5101 #[test_case(0x100000002, 0x100000004, 18245498089483734664)] #[test_case(0x100000004, 0x100000002, 6092969436446403628)] #[test_case(0x100000002, 0x100000006, 16598193894146733116)] #[test_case(0x100000006, 0x100000002, 8940776276357560353)] #[test_case(0x100000000, 0x100000008, 1288053912680171784)] #[test_case(0x100000008, 0x100000000, 4652742827052033592)] #[test_case(0x100000004, 0x200000004, 8833460765081683332)] #[test_case(0x200000004, 0x100000004, 11837649335115988407)] fn test_memmove_success(dst: u64, src: u64, expected_hash: u64) {
5110 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5111 let mut mem = (0..24).collect::<Vec<u8>>();
5112 let config = Config::default();
5113 let mut memory_mapping = MemoryMapping::new(
5114 vec![
5115 MemoryRegion::new_writable(&mut mem[..12], 0x100000000),
5116 MemoryRegion::new_writable(&mut mem[12..], 0x200000000),
5117 ],
5118 &config,
5119 SBPFVersion::V3,
5120 )
5121 .unwrap();
5122
5123 let result =
5124 SyscallMemmove::rust(&mut invoke_context, dst, src, 4, 0, 0, &mut memory_mapping);
5125 result.unwrap();
5126 let mut hasher = DefaultHasher::new();
5127 mem.hash(&mut hasher);
5128 assert_eq!(hasher.finish(), expected_hash);
5129 }
5130
5131 #[test_case(0x100000002, 0x00, 6070675560359421890)]
5132 #[test_case(0x100000002, 0xFF, 3413209638111181029)]
5133 fn test_memset_success(dst: u64, value: u64, expected_hash: u64) {
5134 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5135 let mut mem = (0..12).collect::<Vec<u8>>();
5136 let config = Config::default();
5137 let mut memory_mapping = MemoryMapping::new(
5138 vec![MemoryRegion::new_writable(&mut mem, 0x100000000)],
5139 &config,
5140 SBPFVersion::V3,
5141 )
5142 .unwrap();
5143
5144 let result = SyscallMemset::rust(
5145 &mut invoke_context,
5146 dst,
5147 value,
5148 4,
5149 0,
5150 0,
5151 &mut memory_mapping,
5152 );
5153 result.unwrap();
5154 let mut hasher = DefaultHasher::new();
5155 mem.hash(&mut hasher);
5156 assert_eq!(hasher.finish(), expected_hash);
5157 }
5158
5159 #[test_case(0x100000002, 0x100000004)] #[test_case(0x100000004, 0x100000002)] fn test_memcpy_overlapping(dst: u64, src: u64) {
5162 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5163 let mut mem = (0..12).collect::<Vec<u8>>();
5164 let config = Config::default();
5165 let mut memory_mapping = MemoryMapping::new(
5166 vec![MemoryRegion::new_writable(&mut mem, 0x100000000)],
5167 &config,
5168 SBPFVersion::V3,
5169 )
5170 .unwrap();
5171
5172 let result =
5173 SyscallMemcpy::rust(&mut invoke_context, dst, src, 4, 0, 0, &mut memory_mapping);
5174 assert_matches!(
5175 result,
5176 Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
5177 );
5178 }
5179
5180 #[test_case(0xFFFFFFFFF, 0x100000006, 0xFFFFFFFFF)] #[test_case(0x100000010, 0x100000006, 0x100000010)] #[test_case(0x100000002, 0xFFFFFFFFF, 0xFFFFFFFFF)] #[test_case(0x100000002, 0x100000010, 0x100000010)] fn test_memops_access_violation(dst: u64, src: u64, fault_address: u64) {
5185 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5186 let mut mem = (0..12).collect::<Vec<u8>>();
5187 let config = Config::default();
5188 let mut memory_mapping = MemoryMapping::new(
5189 vec![MemoryRegion::new_writable(&mut mem, 0x100000000)],
5190 &config,
5191 SBPFVersion::V3,
5192 )
5193 .unwrap();
5194
5195 let result =
5196 SyscallMemcpy::rust(&mut invoke_context, dst, src, 4, 0, 0, &mut memory_mapping);
5197 assert_access_violation!(result, fault_address, 4);
5198 let result =
5199 SyscallMemmove::rust(&mut invoke_context, dst, src, 4, 0, 0, &mut memory_mapping);
5200 assert_access_violation!(result, fault_address, 4);
5201 let result =
5202 SyscallMemcmp::rust(&mut invoke_context, dst, src, 4, 0, 0, &mut memory_mapping);
5203 assert_access_violation!(result, fault_address, 4);
5204 }
5205
5206 #[test_case(0xFFFFFFFFF)] #[test_case(0x100000010)] fn test_memset_access_violation(dst: u64) {
5209 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5210 let mut mem = (0..12).collect::<Vec<u8>>();
5211 let config = Config::default();
5212 let mut memory_mapping = MemoryMapping::new(
5213 vec![MemoryRegion::new_writable(&mut mem, 0x100000000)],
5214 &config,
5215 SBPFVersion::V3,
5216 )
5217 .unwrap();
5218
5219 let result = SyscallMemset::rust(&mut invoke_context, dst, 0, 4, 0, 0, &mut memory_mapping);
5220 assert_access_violation!(result, dst, 4);
5221 }
5222
5223 #[test]
5224 fn test_memcmp_result_access_violation() {
5225 prepare_mockup!(invoke_context, program_id, bpf_loader::id());
5226 let mem = (0..12).collect::<Vec<u8>>();
5227 let config = Config::default();
5228 let mut memory_mapping = MemoryMapping::new(
5229 vec![MemoryRegion::new_readonly(&mem, 0x100000000)],
5230 &config,
5231 SBPFVersion::V3,
5232 )
5233 .unwrap();
5234
5235 let result = SyscallMemcmp::rust(
5236 &mut invoke_context,
5237 0x100000000,
5238 0x100000000,
5239 4,
5240 0x100000000,
5241 0,
5242 &mut memory_mapping,
5243 );
5244 assert_access_violation!(result, 0x100000000, 4);
5245 }
5246}