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