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