agave_syscalls/
lib.rs

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