atlas_syscalls/
lib.rs

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
70/// Maximum signers
71const MAX_SIGNERS: usize = 16;
72
73/// Error definitions
74#[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// The VmSlice class is used for cases when you need a slice that is stored in the BPF
229// interpreter's virtual address space. Because this source code can be compiled with
230// addresses of different bit depths, we cannot assume that the 64-bit BPF interpreter's
231// pointer sizes can be mapped to physical pointer sizes. In particular, if you need a
232// slice-of-slices in the virtual space, the inner slices will be different sizes in a
233// 32-bit app build than in the 64-bit virtual space. Therefore instead of a slice-of-slices,
234// you should implement a slice-of-VmSlices, which can then use VmSlice::translate() to
235// map to the physical address.
236// This class must consist only of 16 bytes: a u64 ptr and a u64 len, to match the 64-bit
237// implementation of a slice in Rust. The PhantomData entry takes up 0 bytes.
238
239#[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    /// Adjust the length of the vector. This is unchecked, and it assumes that the pointer
267    /// points to valid memory of the correct length after vm-translation.
268    pub fn resize(&mut self, len: u64) {
269        self.len = len;
270    }
271
272    /// Returns a slice using a mapped physical address
273    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        // Warning, do not use `Config::default()` so that configuration here is explicit.
350    };
351    let mut result = BuiltinProgram::new_loader(config);
352
353    // Abort
354    result.register_function("abort", SyscallAbort::vm)?;
355
356    // Panic
357    result.register_function("sol_panic_", SyscallPanic::vm)?;
358
359    // Logging
360    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    // Program defined addresses (PDA)
366    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    // Sha256
376    result.register_function("sol_sha256", SyscallHash::vm::<Sha256Hasher>)?;
377
378    // Keccak256
379    result.register_function("sol_keccak256", SyscallHash::vm::<Keccak256Hasher>)?;
380
381    // Secp256k1 Recover
382    result.register_function("sol_secp256k1_recover", SyscallSecp256k1Recover::vm)?;
383
384    // Blake3
385    register_feature_gated_function!(
386        result,
387        blake3_syscall_enabled,
388        "sol_blake3",
389        SyscallHash::vm::<Blake3Hasher>,
390    )?;
391
392    // Elliptic Curve Operations
393    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    // Sysvars
413    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    // Memory ops
439    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    // Processed sibling instructions
445    result.register_function(
446        "sol_get_processed_sibling_instruction",
447        SyscallGetProcessedSiblingInstruction::vm,
448    )?;
449
450    // Stack height
451    result.register_function("sol_get_stack_height", SyscallGetStackHeight::vm)?;
452
453    // Return data
454    result.register_function("sol_set_return_data", SyscallSetReturnData::vm)?;
455    result.register_function("sol_get_return_data", SyscallGetReturnData::vm)?;
456
457    // Cross-program invocation
458    result.register_function("sol_invoke_signed_c", SyscallInvokeSignedC::vm)?;
459    result.register_function("sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)?;
460
461    // Memory allocator
462    register_feature_gated_function!(
463        result,
464        !disable_deploy_of_alloc_free_syscall,
465        "sol_alloc_free_",
466        SyscallAllocFree::vm,
467    )?;
468
469    // Alt_bn128
470    register_feature_gated_function!(
471        result,
472        enable_alt_bn128_syscall,
473        "sol_alt_bn128_group_op",
474        SyscallAltBn128::vm,
475    )?;
476
477    // Big_mod_exp
478    register_feature_gated_function!(
479        result,
480        enable_big_mod_exp_syscall,
481        "sol_big_mod_exp",
482        SyscallBigModExp::vm,
483    )?;
484
485    // Poseidon
486    register_feature_gated_function!(
487        result,
488        enable_poseidon_syscall,
489        "sol_poseidon",
490        SyscallPoseidon::vm,
491    )?;
492
493    // Accessing remaining compute units
494    register_feature_gated_function!(
495        result,
496        remaining_compute_units_syscall_enabled,
497        "sol_remaining_compute_units",
498        SyscallRemainingComputeUnits::vm
499    )?;
500
501    // Alt_bn128_compression
502    register_feature_gated_function!(
503        result,
504        enable_alt_bn128_compression_syscall,
505        "sol_alt_bn128_compression",
506        SyscallAltBn128Compression::vm,
507    )?;
508
509    // Sysvar getter
510    register_feature_gated_function!(
511        result,
512        get_sysvar_syscall_enabled,
513        "sol_get_sysvar",
514        SyscallGetSysvar::vm,
515    )?;
516
517    // Get Epoch Stake
518    register_feature_gated_function!(
519        result,
520        enable_get_epoch_stake_syscall,
521        "sol_get_epoch_stake",
522        SyscallGetEpochStake::vm,
523    )?;
524
525    // Log data
526    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, // To be deactivated once we have BTF inference and verification
539        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        // Warning, do not use `Config::default()` so that configuration here is explicit.
551    };
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// Do not use this directly
563#[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// Do not use this directly
574#[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// Do not use this directly
594#[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
637/// Take a virtual pointer to a string (points to SBF VM memory space), translate it
638/// pass it to a user-defined work function
639fn 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
653// Do not use this directly
654fn 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}
661// Do not use this directly
662fn 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// No other translated references can be live when calling this.
707// Meaning it should generally be at the beginning or end of a syscall and
708// it should only be called once with all translations passed in one call.
709#[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        // This ensures that all the parameters are collected first so that if they depend on previous translations
745        $(let $binding = ($vm_addr $(, $element_count)?);)+
746        // they are not invalidated by the following translations here:
747        $(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    /// Abort syscall functions, called when the SBF program calls `abort()`
765    /// LLVM will insert calls to `abort()` if it detects an untenable situation,
766    /// `abort()` is not intended to be called explicitly by the program.
767    /// Causes the SBF program to be halted immediately
768    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    /// Panic syscall function, called when the SBF program calls 'sol_panic_()`
784    /// Causes the SBF program to be halted immediately
785    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    /// Dynamic memory allocation syscall called when the SBF program calls
809    /// `sol_alloc_free_()`.  The allocator is expected to allocate/free
810    /// from/to a given chunk of memory and enforce size restrictions.  The
811    /// memory chunk is given to the allocator during allocator creation and
812    /// information about that memory (start address and size) is passed
813    /// to the VM to use for enforcement.
814    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            // Unimplemented
840            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    /// Create a program address
872    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    /// Create a program address
910    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    /// secp256k1_recover
962    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    // Elliptic Curve Point Validation
1020    //
1021    // Currently, only curve25519 Edwards and Ristretto representations are supported
1022    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    // Elliptic Curve Group Operations
1083    //
1084    // Currently, only curve25519 Edwards and Ristretto representations are supported
1085    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    // Elliptic Curve Multiscalar Multiplication
1311    //
1312    // Currently, only curve25519 Edwards and Ristretto representations are supported
1313    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    /// Set return data
1428    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    /// Get return data
1476    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        // Return the actual length, rather the length returned
1518        Ok(return_data.len() as u64)
1519    }
1520);
1521
1522declare_builtin_function!(
1523    /// Get a processed sigling instruction
1524    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        // Reverse iterate through the instruction trace,
1539        // ignoring anything except instructions on the same level
1540        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                // Marks result_header used. It had to be in translate_mut!() for the overlap checks.
1582                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    /// Get current call stack height
1612    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    /// alt_bn128 group operations
1632    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        // This can never happen and should be removed when the
1722        // simplify_alt_bn128_syscall_error_codes feature gets activated
1723        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    /// Big integer modular exponentiation
1734    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        // the compute units are calculated by the quadratic equation `0.5 input_len^2 + 190`
1762        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    // Poseidon
1809    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    /// Read remaining compute units
1879    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    /// alt_bn128 g1 and g2 compression and decompression
1899    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    // Generic Hashing Syscall
2018    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    // Get Epoch Stake Syscall
2080    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            // As specified by SIMD-0133: If `var_addr` is a null pointer:
2094            //
2095            // Compute units:
2096            //
2097            // ```
2098            // syscall_base
2099            // ```
2100            let compute_units = compute_cost.syscall_base_cost;
2101            consume_compute_meter(invoke_context, compute_units)?;
2102            //
2103            // Control flow:
2104            //
2105            // - The syscall aborts the virtual machine if:
2106            //     - Compute budget is exceeded.
2107            // - Otherwise, the syscall returns a `u64` integer representing the total active
2108            //   stake on the cluster for the current epoch.
2109            Ok(invoke_context.get_epoch_stake())
2110        } else {
2111            // As specified by SIMD-0133: If `var_addr` is _not_ a null pointer:
2112            //
2113            // Compute units:
2114            //
2115            // ```
2116            // syscall_base + floor(PUBKEY_BYTES/cpi_bytes_per_unit) + mem_op_base
2117            // ```
2118            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            //
2128            // Control flow:
2129            //
2130            // - The syscall aborts the virtual machine if:
2131            //     - Not all bytes in VM memory range `[vote_addr, vote_addr + 32)` are
2132            //       readable.
2133            //     - Compute budget is exceeded.
2134            // - Otherwise, the syscall returns a `u64` integer representing the total active
2135            //   stake delegated to the vote account at the provided address.
2136            //   If the provided vote address corresponds to an account that is not a vote
2137            //   account or does not exist, the syscall will return `0` for active stake.
2138            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        // Pubkey
2282        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        // Instruction
2294        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        // zero len
2318        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        // u8
2333        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        // u64
2355        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        // Pubkeys
2373        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(); // Both should point to same place
2390        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, // AccessViolation
2488            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, // AccessViolation
2499            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, // AccessViolation
2578            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        // large alloc
2644        {
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        // many small unaligned allocs
2679        {
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        // many small aligned allocs
2699        {
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        // aligned allocs
2719
2720        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, // AccessViolation
2802            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, // AccessViolation
2813            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, // AccessViolation
2824            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        // test Edwards
3452        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, // below maximum vector length
3459            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, // above maximum vector length
3477            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        // test Ristretto
3487        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, // below maximum vector length
3494            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, // above maximum vector length
3512            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        // Test clock sysvar
3617        {
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        // Test epoch_schedule sysvar
3675        {
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            // clone is to zero the alignment padding
3742            assert!(are_bytes_equal(
3743                &epochschedule_from_buf.clone(),
3744                &clean_epochschedule
3745            ));
3746        }
3747
3748        // Test fees sysvar
3749        {
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            // fees sysvar is not accessible via sol_get_sysvar so nothing further to test
3780        }
3781
3782        // Test rent sysvar
3783        {
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            // clone is to zero the alignment padding
3837            assert!(are_bytes_equal(&rent_from_buf.clone(), &clean_rent));
3838        }
3839
3840        // Test epoch rewards sysvar
3841        {
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            // clone is to zero the alignment padding
3903            assert!(are_bytes_equal(&rewards_from_buf.clone(), &clean_rewards));
3904        }
3905
3906        // Test last restart slot sysvar
3907        {
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            // start without the clock sysvar because we expect to hit specific errors before loading it
4130            with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4131
4132            // Abort: "Not all bytes in VM memory range `[sysvar_id, sysvar_id + 32)` are readable."
4133            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            // Abort: "Not all bytes in VM memory range `[var_addr, var_addr + length)` are writable."
4151            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            // Abort: "`offset + length` is not in `[0, 2^64)`."
4186            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            // "`var_addr + length` is not in `[0, 2^64)`" is theoretically impossible to trigger
4204            // because if the sum extended outside u64::MAX then it would not be writable and translate would fail
4205
4206            // "`2` if the sysvar data is not present in the Sysvar Cache."
4207            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            // "`1` if `offset + length` is greater than the length of the sysvar data."
4230            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            // and now lets succeed
4245            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        // These tests duplicate the direct tests in atlas_pubkey
4562
4563        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; // one per seed
4670
4671        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        // Test that SyscallBigModExp succeeds with the maximum param size
4758        {
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(&params_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        // Test that SyscallBigModExp fails when the maximum param size is exceeded
4800        {
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(&params_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            // Vote accounts are not needed for this test.
4860        }
4861
4862        // Compute units, as specified by SIMD-0133.
4863        // cu = syscall_base_cost
4864        let expected_cus = compute_cost.syscall_base_cost;
4865
4866        // Set the compute budget to the expected CUs to ensure the syscall
4867        // doesn't exceed the expected usage.
4868        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            // Total stake is not needed for this test.
4911            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        // Compute units, as specified by SIMD-0133.
4921        // cu = syscall_base_cost
4922        //     + floor(32/cpi_bytes_per_unit)
4923        //     + mem_op_base_cost
4924        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        // Set the compute budget to the expected CUs to ensure the syscall
4929        // doesn't exceed the expected usage.
4930        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            // The syscall aborts the virtual machine if not all bytes in VM
4944            // memory range `[vote_addr, vote_addr + 32)` are readable.
4945            let vote_address_var = 0x100000000;
4946
4947            let mut memory_mapping = MemoryMapping::new(
4948                vec![
4949                    // Invalid read-only memory region.
4950                    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            // Otherwise, the syscall returns a `u64` integer representing the
4973            // total active stake delegated to the vote account at the provided
4974            // address.
4975            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            // If the provided vote address corresponds to an account that is
5004            // not a vote account or does not exist, the syscall will write
5005            // `0` for active stake.
5006            let vote_address_var = 0x100000000;
5007            let not_a_vote_address = Pubkey::new_unique(); // Not a vote account.
5008
5009            let mut memory_mapping = MemoryMapping::new(
5010                vec![MemoryRegion::new_readonly(
5011                    bytes_of(&not_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); // `0` for active stake.
5031        }
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])] // Intra region match
5067    #[test_case(0x100000003, 0x100000004, &[0xFF, 0xFF, 0xFF, 0xFF])] // Intra region down
5068    #[test_case(0x100000005, 0x100000004, &[0x01, 0x00, 0x00, 0x00])] // Intra region up
5069    #[test_case(0x100000004, 0x200000004, &[0x00, 0x00, 0x00, 0x00])] // Inter region match
5070    #[test_case(0x100000003, 0x200000004, &[0xFF, 0xFF, 0xFF, 0xFF])] // Inter region down
5071    #[test_case(0x100000005, 0x200000004, &[0x01, 0x00, 0x00, 0x00])] // Inter region up
5072    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)] // Down overlapping
5102    #[test_case(0x100000004, 0x100000002, 6092969436446403628)] // Up overlapping
5103    #[test_case(0x100000002, 0x100000006, 16598193894146733116)] // Down touching
5104    #[test_case(0x100000006, 0x100000002, 8940776276357560353)] // Up touching
5105    #[test_case(0x100000000, 0x100000008, 1288053912680171784)] // Down apart
5106    #[test_case(0x100000008, 0x100000000, 4652742827052033592)] // Up apart
5107    #[test_case(0x100000004, 0x200000004, 8833460765081683332)] // Down inter region
5108    #[test_case(0x200000004, 0x100000004, 11837649335115988407)] // Up inter region
5109    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)] // Down overlapping
5160    #[test_case(0x100000004, 0x100000002)] // Up overlapping
5161    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)] // Dst lower bound
5181    #[test_case(0x100000010, 0x100000006, 0x100000010)] // Dst upper bound
5182    #[test_case(0x100000002, 0xFFFFFFFFF, 0xFFFFFFFFF)] // Src lower bound
5183    #[test_case(0x100000002, 0x100000010, 0x100000010)] // Src upper bound
5184    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)] // Dst lower bound
5207    #[test_case(0x100000010)] // Dst upper bound
5208    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}