clone_solana_bpf_loader_program/syscalls/
mod.rs

1pub use self::{
2    cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust},
3    logging::{
4        SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64,
5    },
6    mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
7    sysvar::{
8        SyscallGetClockSysvar, SyscallGetEpochRewardsSysvar, SyscallGetEpochScheduleSysvar,
9        SyscallGetFeesSysvar, SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
10        SyscallGetSysvar,
11    },
12};
13#[allow(deprecated)]
14use {
15    crate::syscalls::mem_ops::is_nonoverlapping,
16    clone_agave_feature_set::{
17        self as feature_set, abort_on_invalid_curve, blake3_syscall_enabled,
18        bpf_account_data_direct_mapping, curve25519_syscall_enabled,
19        disable_deploy_of_alloc_free_syscall, disable_fees_sysvar, disable_sbpf_v0_execution,
20        enable_alt_bn128_compression_syscall, enable_alt_bn128_syscall, enable_big_mod_exp_syscall,
21        enable_get_epoch_stake_syscall, enable_poseidon_syscall,
22        enable_sbpf_v1_deployment_and_execution, enable_sbpf_v2_deployment_and_execution,
23        enable_sbpf_v3_deployment_and_execution, get_sysvar_syscall_enabled,
24        last_restart_slot_sysvar, reenable_sbpf_v0_execution,
25        remaining_compute_units_syscall_enabled, FeatureSet,
26    },
27    clone_agave_precompiles::is_precompile,
28    clone_solana_account_info::AccountInfo,
29    clone_solana_big_mod_exp::{big_mod_exp, BigModExpParams},
30    clone_solana_blake3_hasher as blake3,
31    clone_solana_bn254::prelude::{
32        alt_bn128_addition, alt_bn128_multiplication, alt_bn128_multiplication_128,
33        alt_bn128_pairing, AltBn128Error, ALT_BN128_ADDITION_OUTPUT_LEN,
34        ALT_BN128_MULTIPLICATION_OUTPUT_LEN, ALT_BN128_PAIRING_ELEMENT_LEN,
35        ALT_BN128_PAIRING_OUTPUT_LEN,
36    },
37    clone_solana_compute_budget::compute_budget::ComputeBudget,
38    clone_solana_cpi::MAX_RETURN_DATA,
39    clone_solana_hash::Hash,
40    clone_solana_instruction::{error::InstructionError, AccountMeta, ProcessedSiblingInstruction},
41    clone_solana_keccak_hasher as keccak,
42    clone_solana_log_collector::{ic_logger_msg, ic_msg},
43    clone_solana_poseidon as poseidon,
44    clone_solana_program_entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS},
45    clone_solana_program_runtime::{invoke_context::InvokeContext, stable_log},
46    clone_solana_pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN, PUBKEY_BYTES},
47    clone_solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, native_loader},
48    clone_solana_secp256k1_recover::{
49        Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
50    },
51    clone_solana_sha256_hasher::Hasher,
52    clone_solana_sysvar::Sysvar,
53    clone_solana_sysvar_id::SysvarId,
54    clone_solana_timings::ExecuteTimings,
55    clone_solana_transaction_context::{IndexOfAccount, InstructionAccount},
56    clone_solana_type_overrides::sync::Arc,
57    solana_sbpf::{
58        declare_builtin_function,
59        memory_region::{AccessType, MemoryMapping},
60        program::{BuiltinProgram, SBPFVersion},
61        vm::Config,
62    },
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 clone_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 clone_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 clone_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 clone_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 clone_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 clone_solana_sysvar::fees::Fees;
2242    use {
2243        super::*,
2244        crate::mock_create_vm,
2245        assert_matches::assert_matches,
2246        clone_solana_account::{create_account_shared_data_for_test, AccountSharedData},
2247        clone_solana_clock::Clock,
2248        clone_solana_epoch_rewards::EpochRewards,
2249        clone_solana_epoch_schedule::EpochSchedule,
2250        clone_solana_fee_calculator::FeeCalculator,
2251        clone_solana_hash::HASH_BYTES,
2252        clone_solana_instruction::Instruction,
2253        clone_solana_last_restart_slot::LastRestartSlot,
2254        clone_solana_program::program::check_type_assumptions,
2255        clone_solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
2256        clone_solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable, sysvar},
2257        clone_solana_sha256_hasher::hashv,
2258        clone_solana_slot_hashes::{self as slot_hashes, SlotHashes},
2259        clone_solana_stable_layout::stable_instruction::StableInstruction,
2260        clone_solana_sysvar::stake_history::{self, StakeHistory, StakeHistoryEntry},
2261        core::slice,
2262        solana_sbpf::{
2263            error::EbpfError, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
2264        },
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 = clone_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            clone_solana_pubkey::new_rand(),
2369            &"foobar",
2370            vec![AccountMeta::new(clone_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![clone_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() = clone_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                clone_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                clone_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                clone_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                clone_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 clone_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 clone_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 clone_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 clone_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 clone_solana_curve25519::curve_syscall_traits::{
3361            CURVE25519_EDWARDS, CURVE25519_RISTRETTO,
3362        };
3363
3364        let config = Config::default();
3365        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3366
3367        let scalar_a: [u8; 32] = [
3368            254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3369            78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3370        ];
3371        let scalar_b: [u8; 32] = [
3372            254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3373            78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3374        ];
3375
3376        let scalars = [scalar_a, scalar_b];
3377        let scalars_va = 0x100000000;
3378
3379        let edwards_point_x: [u8; 32] = [
3380            252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3381            53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3382        ];
3383        let edwards_point_y: [u8; 32] = [
3384            10, 111, 8, 236, 97, 189, 124, 69, 89, 176, 222, 39, 199, 253, 111, 11, 248, 186, 128,
3385            90, 120, 128, 248, 210, 232, 183, 93, 104, 111, 150, 7, 241,
3386        ];
3387        let edwards_points = [edwards_point_x, edwards_point_y];
3388        let edwards_points_va = 0x200000000;
3389
3390        let ristretto_point_x: [u8; 32] = [
3391            130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3392            179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3393        ];
3394        let ristretto_point_y: [u8; 32] = [
3395            152, 156, 155, 197, 152, 232, 92, 206, 219, 159, 193, 134, 121, 128, 139, 36, 56, 191,
3396            51, 143, 72, 204, 87, 76, 110, 124, 101, 96, 238, 158, 42, 108,
3397        ];
3398        let ristretto_points = [ristretto_point_x, ristretto_point_y];
3399        let ristretto_points_va = 0x300000000;
3400
3401        let mut result_point: [u8; 32] = [0; 32];
3402        let result_point_va = 0x400000000;
3403
3404        let mut memory_mapping = MemoryMapping::new(
3405            vec![
3406                MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3407                MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3408                MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3409                MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3410            ],
3411            &config,
3412            SBPFVersion::V3,
3413        )
3414        .unwrap();
3415
3416        invoke_context.mock_set_remaining(
3417            invoke_context
3418                .get_compute_budget()
3419                .curve25519_edwards_msm_base_cost
3420                + invoke_context
3421                    .get_compute_budget()
3422                    .curve25519_edwards_msm_incremental_cost
3423                + invoke_context
3424                    .get_compute_budget()
3425                    .curve25519_ristretto_msm_base_cost
3426                + invoke_context
3427                    .get_compute_budget()
3428                    .curve25519_ristretto_msm_incremental_cost,
3429        );
3430
3431        let result = SyscallCurveMultiscalarMultiplication::rust(
3432            &mut invoke_context,
3433            CURVE25519_EDWARDS,
3434            scalars_va,
3435            edwards_points_va,
3436            2,
3437            result_point_va,
3438            &mut memory_mapping,
3439        );
3440
3441        assert_eq!(0, result.unwrap());
3442        let expected_product = [
3443            30, 174, 168, 34, 160, 70, 63, 166, 236, 18, 74, 144, 185, 222, 208, 243, 5, 54, 223,
3444            172, 185, 75, 244, 26, 70, 18, 248, 46, 207, 184, 235, 60,
3445        ];
3446        assert_eq!(expected_product, result_point);
3447
3448        let result = SyscallCurveMultiscalarMultiplication::rust(
3449            &mut invoke_context,
3450            CURVE25519_RISTRETTO,
3451            scalars_va,
3452            ristretto_points_va,
3453            2,
3454            result_point_va,
3455            &mut memory_mapping,
3456        );
3457
3458        assert_eq!(0, result.unwrap());
3459        let expected_product = [
3460            78, 120, 86, 111, 152, 64, 146, 84, 14, 236, 77, 147, 237, 190, 251, 241, 136, 167, 21,
3461            94, 84, 118, 92, 140, 120, 81, 30, 246, 173, 140, 195, 86,
3462        ];
3463        assert_eq!(expected_product, result_point);
3464    }
3465
3466    #[test]
3467    fn test_syscall_multiscalar_multiplication_maximum_length_exceeded() {
3468        use clone_solana_curve25519::curve_syscall_traits::{
3469            CURVE25519_EDWARDS, CURVE25519_RISTRETTO,
3470        };
3471
3472        let config = Config::default();
3473        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
3474
3475        let scalar: [u8; 32] = [
3476            254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
3477            78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
3478        ];
3479        let scalars = [scalar; 513];
3480        let scalars_va = 0x100000000;
3481
3482        let edwards_point: [u8; 32] = [
3483            252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
3484            53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
3485        ];
3486        let edwards_points = [edwards_point; 513];
3487        let edwards_points_va = 0x200000000;
3488
3489        let ristretto_point: [u8; 32] = [
3490            130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
3491            179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
3492        ];
3493        let ristretto_points = [ristretto_point; 513];
3494        let ristretto_points_va = 0x300000000;
3495
3496        let mut result_point: [u8; 32] = [0; 32];
3497        let result_point_va = 0x400000000;
3498
3499        let mut memory_mapping = MemoryMapping::new(
3500            vec![
3501                MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
3502                MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
3503                MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
3504                MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
3505            ],
3506            &config,
3507            SBPFVersion::V3,
3508        )
3509        .unwrap();
3510
3511        // test Edwards
3512        invoke_context.mock_set_remaining(500_000);
3513        let result = SyscallCurveMultiscalarMultiplication::rust(
3514            &mut invoke_context,
3515            CURVE25519_EDWARDS,
3516            scalars_va,
3517            edwards_points_va,
3518            512, // below maximum vector length
3519            result_point_va,
3520            &mut memory_mapping,
3521        );
3522
3523        assert_eq!(0, result.unwrap());
3524        let expected_product = [
3525            20, 146, 226, 37, 22, 61, 86, 249, 208, 40, 38, 11, 126, 101, 10, 82, 81, 77, 88, 209,
3526            15, 76, 82, 251, 180, 133, 84, 243, 162, 0, 11, 145,
3527        ];
3528        assert_eq!(expected_product, result_point);
3529
3530        invoke_context.mock_set_remaining(500_000);
3531        let result = SyscallCurveMultiscalarMultiplication::rust(
3532            &mut invoke_context,
3533            CURVE25519_EDWARDS,
3534            scalars_va,
3535            edwards_points_va,
3536            513, // above maximum vector length
3537            result_point_va,
3538            &mut memory_mapping,
3539        )
3540        .unwrap_err()
3541        .downcast::<SyscallError>()
3542        .unwrap();
3543
3544        assert_eq!(*result, SyscallError::InvalidLength);
3545
3546        // test Ristretto
3547        invoke_context.mock_set_remaining(500_000);
3548        let result = SyscallCurveMultiscalarMultiplication::rust(
3549            &mut invoke_context,
3550            CURVE25519_RISTRETTO,
3551            scalars_va,
3552            ristretto_points_va,
3553            512, // below maximum vector length
3554            result_point_va,
3555            &mut memory_mapping,
3556        );
3557
3558        assert_eq!(0, result.unwrap());
3559        let expected_product = [
3560            146, 224, 127, 193, 252, 64, 196, 181, 246, 104, 27, 116, 183, 52, 200, 239, 2, 108,
3561            21, 27, 97, 44, 95, 65, 26, 218, 223, 39, 197, 132, 51, 49,
3562        ];
3563        assert_eq!(expected_product, result_point);
3564
3565        invoke_context.mock_set_remaining(500_000);
3566        let result = SyscallCurveMultiscalarMultiplication::rust(
3567            &mut invoke_context,
3568            CURVE25519_RISTRETTO,
3569            scalars_va,
3570            ristretto_points_va,
3571            513, // above maximum vector length
3572            result_point_va,
3573            &mut memory_mapping,
3574        )
3575        .unwrap_err()
3576        .downcast::<SyscallError>()
3577        .unwrap();
3578
3579        assert_eq!(*result, SyscallError::InvalidLength);
3580    }
3581
3582    fn create_filled_type<T: Default>(zero_init: bool) -> T {
3583        let mut val = T::default();
3584        let p = &mut val as *mut _ as *mut u8;
3585        for i in 0..(size_of::<T>() as isize) {
3586            unsafe {
3587                *p.offset(i) = if zero_init { 0 } else { i as u8 };
3588            }
3589        }
3590        val
3591    }
3592
3593    fn are_bytes_equal<T>(first: &T, second: &T) -> bool {
3594        let p_first = first as *const _ as *const u8;
3595        let p_second = second as *const _ as *const u8;
3596
3597        for i in 0..(size_of::<T>() as isize) {
3598            unsafe {
3599                if *p_first.offset(i) != *p_second.offset(i) {
3600                    return false;
3601                }
3602            }
3603        }
3604        true
3605    }
3606
3607    #[test]
3608    #[allow(deprecated)]
3609    fn test_syscall_get_sysvar() {
3610        let config = Config::default();
3611
3612        let mut src_clock = create_filled_type::<Clock>(false);
3613        src_clock.slot = 1;
3614        src_clock.epoch_start_timestamp = 2;
3615        src_clock.epoch = 3;
3616        src_clock.leader_schedule_epoch = 4;
3617        src_clock.unix_timestamp = 5;
3618
3619        let mut src_epochschedule = create_filled_type::<EpochSchedule>(false);
3620        src_epochschedule.slots_per_epoch = 1;
3621        src_epochschedule.leader_schedule_slot_offset = 2;
3622        src_epochschedule.warmup = false;
3623        src_epochschedule.first_normal_epoch = 3;
3624        src_epochschedule.first_normal_slot = 4;
3625
3626        let mut src_fees = create_filled_type::<Fees>(false);
3627        src_fees.fee_calculator = FeeCalculator {
3628            lamports_per_signature: 1,
3629        };
3630
3631        let mut src_rent = create_filled_type::<Rent>(false);
3632        src_rent.lamports_per_byte_year = 1;
3633        src_rent.exemption_threshold = 2.0;
3634        src_rent.burn_percent = 3;
3635
3636        let mut src_rewards = create_filled_type::<EpochRewards>(false);
3637        src_rewards.distribution_starting_block_height = 42;
3638        src_rewards.num_partitions = 2;
3639        src_rewards.parent_blockhash = Hash::new_from_array([3; 32]);
3640        src_rewards.total_points = 4;
3641        src_rewards.total_rewards = 100;
3642        src_rewards.distributed_rewards = 10;
3643        src_rewards.active = true;
3644
3645        let mut src_restart = create_filled_type::<LastRestartSlot>(false);
3646        src_restart.last_restart_slot = 1;
3647
3648        let transaction_accounts = vec![
3649            (
3650                sysvar::clock::id(),
3651                create_account_shared_data_for_test(&src_clock),
3652            ),
3653            (
3654                sysvar::epoch_schedule::id(),
3655                create_account_shared_data_for_test(&src_epochschedule),
3656            ),
3657            (
3658                sysvar::fees::id(),
3659                create_account_shared_data_for_test(&src_fees),
3660            ),
3661            (
3662                sysvar::rent::id(),
3663                create_account_shared_data_for_test(&src_rent),
3664            ),
3665            (
3666                sysvar::epoch_rewards::id(),
3667                create_account_shared_data_for_test(&src_rewards),
3668            ),
3669            (
3670                sysvar::last_restart_slot::id(),
3671                create_account_shared_data_for_test(&src_restart),
3672            ),
3673        ];
3674        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
3675
3676        // Test clock sysvar
3677        {
3678            let mut got_clock_obj = Clock::default();
3679            let got_clock_obj_va = 0x100000000;
3680
3681            let mut got_clock_buf = vec![0; Clock::size_of()];
3682            let got_clock_buf_va = 0x200000000;
3683            let clock_id_va = 0x300000000;
3684
3685            let mut memory_mapping = MemoryMapping::new(
3686                vec![
3687                    MemoryRegion::new_writable(bytes_of_mut(&mut got_clock_obj), got_clock_obj_va),
3688                    MemoryRegion::new_writable(&mut got_clock_buf, got_clock_buf_va),
3689                    MemoryRegion::new_readonly(&Clock::id().to_bytes(), clock_id_va),
3690                ],
3691                &config,
3692                SBPFVersion::V3,
3693            )
3694            .unwrap();
3695
3696            let result = SyscallGetClockSysvar::rust(
3697                &mut invoke_context,
3698                got_clock_obj_va,
3699                0,
3700                0,
3701                0,
3702                0,
3703                &mut memory_mapping,
3704            );
3705            result.unwrap();
3706            assert_eq!(got_clock_obj, src_clock);
3707
3708            let mut clean_clock = create_filled_type::<Clock>(true);
3709            clean_clock.slot = src_clock.slot;
3710            clean_clock.epoch_start_timestamp = src_clock.epoch_start_timestamp;
3711            clean_clock.epoch = src_clock.epoch;
3712            clean_clock.leader_schedule_epoch = src_clock.leader_schedule_epoch;
3713            clean_clock.unix_timestamp = src_clock.unix_timestamp;
3714            assert!(are_bytes_equal(&got_clock_obj, &clean_clock));
3715
3716            let result = SyscallGetSysvar::rust(
3717                &mut invoke_context,
3718                clock_id_va,
3719                got_clock_buf_va,
3720                0,
3721                Clock::size_of() as u64,
3722                0,
3723                &mut memory_mapping,
3724            );
3725            result.unwrap();
3726
3727            let clock_from_buf = bincode::deserialize::<Clock>(&got_clock_buf).unwrap();
3728
3729            assert_eq!(clock_from_buf, src_clock);
3730            assert!(are_bytes_equal(&clock_from_buf, &clean_clock));
3731        }
3732
3733        // Test epoch_schedule sysvar
3734        {
3735            let mut got_epochschedule_obj = EpochSchedule::default();
3736            let got_epochschedule_obj_va = 0x100000000;
3737
3738            let mut got_epochschedule_buf = vec![0; EpochSchedule::size_of()];
3739            let got_epochschedule_buf_va = 0x200000000;
3740            let epochschedule_id_va = 0x300000000;
3741
3742            let mut memory_mapping = MemoryMapping::new(
3743                vec![
3744                    MemoryRegion::new_writable(
3745                        bytes_of_mut(&mut got_epochschedule_obj),
3746                        got_epochschedule_obj_va,
3747                    ),
3748                    MemoryRegion::new_writable(
3749                        &mut got_epochschedule_buf,
3750                        got_epochschedule_buf_va,
3751                    ),
3752                    MemoryRegion::new_readonly(
3753                        &EpochSchedule::id().to_bytes(),
3754                        epochschedule_id_va,
3755                    ),
3756                ],
3757                &config,
3758                SBPFVersion::V3,
3759            )
3760            .unwrap();
3761
3762            let result = SyscallGetEpochScheduleSysvar::rust(
3763                &mut invoke_context,
3764                got_epochschedule_obj_va,
3765                0,
3766                0,
3767                0,
3768                0,
3769                &mut memory_mapping,
3770            );
3771            result.unwrap();
3772            assert_eq!(got_epochschedule_obj, src_epochschedule);
3773
3774            let mut clean_epochschedule = create_filled_type::<EpochSchedule>(true);
3775            clean_epochschedule.slots_per_epoch = src_epochschedule.slots_per_epoch;
3776            clean_epochschedule.leader_schedule_slot_offset =
3777                src_epochschedule.leader_schedule_slot_offset;
3778            clean_epochschedule.warmup = src_epochschedule.warmup;
3779            clean_epochschedule.first_normal_epoch = src_epochschedule.first_normal_epoch;
3780            clean_epochschedule.first_normal_slot = src_epochschedule.first_normal_slot;
3781            assert!(are_bytes_equal(
3782                &got_epochschedule_obj,
3783                &clean_epochschedule
3784            ));
3785
3786            let result = SyscallGetSysvar::rust(
3787                &mut invoke_context,
3788                epochschedule_id_va,
3789                got_epochschedule_buf_va,
3790                0,
3791                EpochSchedule::size_of() as u64,
3792                0,
3793                &mut memory_mapping,
3794            );
3795            result.unwrap();
3796
3797            let epochschedule_from_buf =
3798                bincode::deserialize::<EpochSchedule>(&got_epochschedule_buf).unwrap();
3799
3800            assert_eq!(epochschedule_from_buf, src_epochschedule);
3801
3802            // clone is to zero the alignment padding
3803            assert!(are_bytes_equal(
3804                &epochschedule_from_buf.clone(),
3805                &clean_epochschedule
3806            ));
3807        }
3808
3809        // Test fees sysvar
3810        {
3811            let mut got_fees = Fees::default();
3812            let got_fees_va = 0x100000000;
3813
3814            let mut memory_mapping = MemoryMapping::new(
3815                vec![MemoryRegion::new_writable(
3816                    bytes_of_mut(&mut got_fees),
3817                    got_fees_va,
3818                )],
3819                &config,
3820                SBPFVersion::V3,
3821            )
3822            .unwrap();
3823
3824            let result = SyscallGetFeesSysvar::rust(
3825                &mut invoke_context,
3826                got_fees_va,
3827                0,
3828                0,
3829                0,
3830                0,
3831                &mut memory_mapping,
3832            );
3833            result.unwrap();
3834            assert_eq!(got_fees, src_fees);
3835
3836            let mut clean_fees = create_filled_type::<Fees>(true);
3837            clean_fees.fee_calculator = src_fees.fee_calculator;
3838            assert!(are_bytes_equal(&got_fees, &clean_fees));
3839
3840            // fees sysvar is not accessible via sol_get_sysvar so nothing further to test
3841        }
3842
3843        // Test rent sysvar
3844        {
3845            let mut got_rent_obj = create_filled_type::<Rent>(true);
3846            let got_rent_obj_va = 0x100000000;
3847
3848            let mut got_rent_buf = vec![0; Rent::size_of()];
3849            let got_rent_buf_va = 0x200000000;
3850            let rent_id_va = 0x300000000;
3851
3852            let mut memory_mapping = MemoryMapping::new(
3853                vec![
3854                    MemoryRegion::new_writable(bytes_of_mut(&mut got_rent_obj), got_rent_obj_va),
3855                    MemoryRegion::new_writable(&mut got_rent_buf, got_rent_buf_va),
3856                    MemoryRegion::new_readonly(&Rent::id().to_bytes(), rent_id_va),
3857                ],
3858                &config,
3859                SBPFVersion::V3,
3860            )
3861            .unwrap();
3862
3863            let result = SyscallGetRentSysvar::rust(
3864                &mut invoke_context,
3865                got_rent_obj_va,
3866                0,
3867                0,
3868                0,
3869                0,
3870                &mut memory_mapping,
3871            );
3872            result.unwrap();
3873            assert_eq!(got_rent_obj, src_rent);
3874
3875            let mut clean_rent = create_filled_type::<Rent>(true);
3876            clean_rent.lamports_per_byte_year = src_rent.lamports_per_byte_year;
3877            clean_rent.exemption_threshold = src_rent.exemption_threshold;
3878            clean_rent.burn_percent = src_rent.burn_percent;
3879            assert!(are_bytes_equal(&got_rent_obj, &clean_rent));
3880
3881            let result = SyscallGetSysvar::rust(
3882                &mut invoke_context,
3883                rent_id_va,
3884                got_rent_buf_va,
3885                0,
3886                Rent::size_of() as u64,
3887                0,
3888                &mut memory_mapping,
3889            );
3890            result.unwrap();
3891
3892            let rent_from_buf = bincode::deserialize::<Rent>(&got_rent_buf).unwrap();
3893
3894            assert_eq!(rent_from_buf, src_rent);
3895
3896            // clone is to zero the alignment padding
3897            assert!(are_bytes_equal(&rent_from_buf.clone(), &clean_rent));
3898        }
3899
3900        // Test epoch rewards sysvar
3901        {
3902            let mut got_rewards_obj = create_filled_type::<EpochRewards>(true);
3903            let got_rewards_obj_va = 0x100000000;
3904
3905            let mut got_rewards_buf = vec![0; EpochRewards::size_of()];
3906            let got_rewards_buf_va = 0x200000000;
3907            let rewards_id_va = 0x300000000;
3908
3909            let mut memory_mapping = MemoryMapping::new(
3910                vec![
3911                    MemoryRegion::new_writable(
3912                        bytes_of_mut(&mut got_rewards_obj),
3913                        got_rewards_obj_va,
3914                    ),
3915                    MemoryRegion::new_writable(&mut got_rewards_buf, got_rewards_buf_va),
3916                    MemoryRegion::new_readonly(&EpochRewards::id().to_bytes(), rewards_id_va),
3917                ],
3918                &config,
3919                SBPFVersion::V3,
3920            )
3921            .unwrap();
3922
3923            let result = SyscallGetEpochRewardsSysvar::rust(
3924                &mut invoke_context,
3925                got_rewards_obj_va,
3926                0,
3927                0,
3928                0,
3929                0,
3930                &mut memory_mapping,
3931            );
3932            result.unwrap();
3933            assert_eq!(got_rewards_obj, src_rewards);
3934
3935            let mut clean_rewards = create_filled_type::<EpochRewards>(true);
3936            clean_rewards.distribution_starting_block_height =
3937                src_rewards.distribution_starting_block_height;
3938            clean_rewards.num_partitions = src_rewards.num_partitions;
3939            clean_rewards.parent_blockhash = src_rewards.parent_blockhash;
3940            clean_rewards.total_points = src_rewards.total_points;
3941            clean_rewards.total_rewards = src_rewards.total_rewards;
3942            clean_rewards.distributed_rewards = src_rewards.distributed_rewards;
3943            clean_rewards.active = src_rewards.active;
3944            assert!(are_bytes_equal(&got_rewards_obj, &clean_rewards));
3945
3946            let result = SyscallGetSysvar::rust(
3947                &mut invoke_context,
3948                rewards_id_va,
3949                got_rewards_buf_va,
3950                0,
3951                EpochRewards::size_of() as u64,
3952                0,
3953                &mut memory_mapping,
3954            );
3955            result.unwrap();
3956
3957            let rewards_from_buf = bincode::deserialize::<EpochRewards>(&got_rewards_buf).unwrap();
3958
3959            assert_eq!(rewards_from_buf, src_rewards);
3960
3961            // clone is to zero the alignment padding
3962            assert!(are_bytes_equal(&rewards_from_buf.clone(), &clean_rewards));
3963        }
3964
3965        // Test last restart slot sysvar
3966        {
3967            let mut got_restart_obj = LastRestartSlot::default();
3968            let got_restart_obj_va = 0x100000000;
3969
3970            let mut got_restart_buf = vec![0; LastRestartSlot::size_of()];
3971            let got_restart_buf_va = 0x200000000;
3972            let restart_id_va = 0x300000000;
3973
3974            let mut memory_mapping = MemoryMapping::new(
3975                vec![
3976                    MemoryRegion::new_writable(
3977                        bytes_of_mut(&mut got_restart_obj),
3978                        got_restart_obj_va,
3979                    ),
3980                    MemoryRegion::new_writable(&mut got_restart_buf, got_restart_buf_va),
3981                    MemoryRegion::new_readonly(&LastRestartSlot::id().to_bytes(), restart_id_va),
3982                ],
3983                &config,
3984                SBPFVersion::V3,
3985            )
3986            .unwrap();
3987
3988            let result = SyscallGetLastRestartSlotSysvar::rust(
3989                &mut invoke_context,
3990                got_restart_obj_va,
3991                0,
3992                0,
3993                0,
3994                0,
3995                &mut memory_mapping,
3996            );
3997            result.unwrap();
3998            assert_eq!(got_restart_obj, src_restart);
3999
4000            let mut clean_restart = create_filled_type::<LastRestartSlot>(true);
4001            clean_restart.last_restart_slot = src_restart.last_restart_slot;
4002            assert!(are_bytes_equal(&got_restart_obj, &clean_restart));
4003
4004            let result = SyscallGetSysvar::rust(
4005                &mut invoke_context,
4006                restart_id_va,
4007                got_restart_buf_va,
4008                0,
4009                LastRestartSlot::size_of() as u64,
4010                0,
4011                &mut memory_mapping,
4012            );
4013            result.unwrap();
4014
4015            let restart_from_buf =
4016                bincode::deserialize::<LastRestartSlot>(&got_restart_buf).unwrap();
4017
4018            assert_eq!(restart_from_buf, src_restart);
4019            assert!(are_bytes_equal(&restart_from_buf, &clean_restart));
4020        }
4021    }
4022
4023    #[test_case(false; "partial")]
4024    #[test_case(true; "full")]
4025    fn test_syscall_get_stake_history(filled: bool) {
4026        let config = Config::default();
4027
4028        let mut src_history = StakeHistory::default();
4029
4030        let epochs = if filled {
4031            stake_history::MAX_ENTRIES + 1
4032        } else {
4033            stake_history::MAX_ENTRIES / 2
4034        } as u64;
4035
4036        for epoch in 1..epochs {
4037            src_history.add(
4038                epoch,
4039                StakeHistoryEntry {
4040                    effective: epoch * 2,
4041                    activating: epoch * 3,
4042                    deactivating: epoch * 5,
4043                },
4044            );
4045        }
4046
4047        let src_history = src_history;
4048
4049        let mut src_history_buf = vec![0; StakeHistory::size_of()];
4050        bincode::serialize_into(&mut src_history_buf, &src_history).unwrap();
4051
4052        let transaction_accounts = vec![(
4053            sysvar::stake_history::id(),
4054            create_account_shared_data_for_test(&src_history),
4055        )];
4056        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4057
4058        {
4059            let mut got_history_buf = vec![0; StakeHistory::size_of()];
4060            let got_history_buf_va = 0x100000000;
4061            let history_id_va = 0x200000000;
4062
4063            let mut memory_mapping = MemoryMapping::new(
4064                vec![
4065                    MemoryRegion::new_writable(&mut got_history_buf, got_history_buf_va),
4066                    MemoryRegion::new_readonly(&StakeHistory::id().to_bytes(), history_id_va),
4067                ],
4068                &config,
4069                SBPFVersion::V3,
4070            )
4071            .unwrap();
4072
4073            let result = SyscallGetSysvar::rust(
4074                &mut invoke_context,
4075                history_id_va,
4076                got_history_buf_va,
4077                0,
4078                StakeHistory::size_of() as u64,
4079                0,
4080                &mut memory_mapping,
4081            );
4082            result.unwrap();
4083
4084            let history_from_buf = bincode::deserialize::<StakeHistory>(&got_history_buf).unwrap();
4085            assert_eq!(history_from_buf, src_history);
4086        }
4087    }
4088
4089    #[test_case(false; "partial")]
4090    #[test_case(true; "full")]
4091    fn test_syscall_get_slot_hashes(filled: bool) {
4092        let config = Config::default();
4093
4094        let mut src_hashes = SlotHashes::default();
4095
4096        let slots = if filled {
4097            slot_hashes::MAX_ENTRIES + 1
4098        } else {
4099            slot_hashes::MAX_ENTRIES / 2
4100        } as u64;
4101
4102        for slot in 1..slots {
4103            src_hashes.add(slot, hashv(&[&slot.to_le_bytes()]));
4104        }
4105
4106        let src_hashes = src_hashes;
4107
4108        let mut src_hashes_buf = vec![0; SlotHashes::size_of()];
4109        bincode::serialize_into(&mut src_hashes_buf, &src_hashes).unwrap();
4110
4111        let transaction_accounts = vec![(
4112            sysvar::slot_hashes::id(),
4113            create_account_shared_data_for_test(&src_hashes),
4114        )];
4115        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4116
4117        {
4118            let mut got_hashes_buf = vec![0; SlotHashes::size_of()];
4119            let got_hashes_buf_va = 0x100000000;
4120            let hashes_id_va = 0x200000000;
4121
4122            let mut memory_mapping = MemoryMapping::new(
4123                vec![
4124                    MemoryRegion::new_writable(&mut got_hashes_buf, got_hashes_buf_va),
4125                    MemoryRegion::new_readonly(&SlotHashes::id().to_bytes(), hashes_id_va),
4126                ],
4127                &config,
4128                SBPFVersion::V3,
4129            )
4130            .unwrap();
4131
4132            let result = SyscallGetSysvar::rust(
4133                &mut invoke_context,
4134                hashes_id_va,
4135                got_hashes_buf_va,
4136                0,
4137                SlotHashes::size_of() as u64,
4138                0,
4139                &mut memory_mapping,
4140            );
4141            result.unwrap();
4142
4143            let hashes_from_buf = bincode::deserialize::<SlotHashes>(&got_hashes_buf).unwrap();
4144            assert_eq!(hashes_from_buf, src_hashes);
4145        }
4146    }
4147
4148    #[test]
4149    fn test_syscall_get_sysvar_errors() {
4150        let config = Config::default();
4151
4152        let mut src_clock = create_filled_type::<Clock>(false);
4153        src_clock.slot = 1;
4154        src_clock.epoch_start_timestamp = 2;
4155        src_clock.epoch = 3;
4156        src_clock.leader_schedule_epoch = 4;
4157        src_clock.unix_timestamp = 5;
4158
4159        let clock_id_va = 0x100000000;
4160
4161        let mut got_clock_buf_rw = vec![0; Clock::size_of()];
4162        let got_clock_buf_rw_va = 0x200000000;
4163
4164        let got_clock_buf_ro = vec![0; Clock::size_of()];
4165        let got_clock_buf_ro_va = 0x300000000;
4166
4167        let mut memory_mapping = MemoryMapping::new(
4168            vec![
4169                MemoryRegion::new_readonly(&Clock::id().to_bytes(), clock_id_va),
4170                MemoryRegion::new_writable(&mut got_clock_buf_rw, got_clock_buf_rw_va),
4171                MemoryRegion::new_readonly(&got_clock_buf_ro, got_clock_buf_ro_va),
4172            ],
4173            &config,
4174            SBPFVersion::V3,
4175        )
4176        .unwrap();
4177
4178        let access_violation_err =
4179            std::mem::discriminant(&EbpfError::AccessViolation(AccessType::Load, 0, 0, ""));
4180
4181        let got_clock_empty = vec![0; Clock::size_of()];
4182
4183        {
4184            // start without the clock sysvar because we expect to hit specific errors before loading it
4185            with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4186
4187            // Abort: "Not all bytes in VM memory range `[sysvar_id, sysvar_id + 32)` are readable."
4188            let e = SyscallGetSysvar::rust(
4189                &mut invoke_context,
4190                clock_id_va + 1,
4191                got_clock_buf_rw_va,
4192                0,
4193                Clock::size_of() as u64,
4194                0,
4195                &mut memory_mapping,
4196            )
4197            .unwrap_err();
4198
4199            assert_eq!(
4200                std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4201                access_violation_err,
4202            );
4203            assert_eq!(got_clock_buf_rw, got_clock_empty);
4204
4205            // Abort: "Not all bytes in VM memory range `[var_addr, var_addr + length)` are writable."
4206            let e = SyscallGetSysvar::rust(
4207                &mut invoke_context,
4208                clock_id_va,
4209                got_clock_buf_rw_va + 1,
4210                0,
4211                Clock::size_of() as u64,
4212                0,
4213                &mut memory_mapping,
4214            )
4215            .unwrap_err();
4216
4217            assert_eq!(
4218                std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4219                access_violation_err,
4220            );
4221            assert_eq!(got_clock_buf_rw, got_clock_empty);
4222
4223            let e = SyscallGetSysvar::rust(
4224                &mut invoke_context,
4225                clock_id_va,
4226                got_clock_buf_ro_va,
4227                0,
4228                Clock::size_of() as u64,
4229                0,
4230                &mut memory_mapping,
4231            )
4232            .unwrap_err();
4233
4234            assert_eq!(
4235                std::mem::discriminant(e.downcast_ref::<EbpfError>().unwrap()),
4236                access_violation_err,
4237            );
4238            assert_eq!(got_clock_buf_rw, got_clock_empty);
4239
4240            // Abort: "`offset + length` is not in `[0, 2^64)`."
4241            let e = SyscallGetSysvar::rust(
4242                &mut invoke_context,
4243                clock_id_va,
4244                got_clock_buf_rw_va,
4245                u64::MAX - Clock::size_of() as u64 / 2,
4246                Clock::size_of() as u64,
4247                0,
4248                &mut memory_mapping,
4249            )
4250            .unwrap_err();
4251
4252            assert_eq!(
4253                *e.downcast_ref::<InstructionError>().unwrap(),
4254                InstructionError::ArithmeticOverflow,
4255            );
4256            assert_eq!(got_clock_buf_rw, got_clock_empty);
4257
4258            // "`var_addr + length` is not in `[0, 2^64)`" is theoretically impossible to trigger
4259            // because if the sum extended outside u64::MAX then it would not be writable and translate would fail
4260
4261            // "`2` if the sysvar data is not present in the Sysvar Cache."
4262            let result = SyscallGetSysvar::rust(
4263                &mut invoke_context,
4264                clock_id_va,
4265                got_clock_buf_rw_va,
4266                0,
4267                Clock::size_of() as u64,
4268                0,
4269                &mut memory_mapping,
4270            )
4271            .unwrap();
4272
4273            assert_eq!(result, 2);
4274            assert_eq!(got_clock_buf_rw, got_clock_empty);
4275        }
4276
4277        {
4278            let transaction_accounts = vec![(
4279                sysvar::clock::id(),
4280                create_account_shared_data_for_test(&src_clock),
4281            )];
4282            with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4283
4284            // "`1` if `offset + length` is greater than the length of the sysvar data."
4285            let result = SyscallGetSysvar::rust(
4286                &mut invoke_context,
4287                clock_id_va,
4288                got_clock_buf_rw_va,
4289                1,
4290                Clock::size_of() as u64,
4291                0,
4292                &mut memory_mapping,
4293            )
4294            .unwrap();
4295
4296            assert_eq!(result, 1);
4297            assert_eq!(got_clock_buf_rw, got_clock_empty);
4298
4299            // and now lets succeed
4300            SyscallGetSysvar::rust(
4301                &mut invoke_context,
4302                clock_id_va,
4303                got_clock_buf_rw_va,
4304                0,
4305                Clock::size_of() as u64,
4306                0,
4307                &mut memory_mapping,
4308            )
4309            .unwrap();
4310
4311            let clock_from_buf = bincode::deserialize::<Clock>(&got_clock_buf_rw).unwrap();
4312
4313            assert_eq!(clock_from_buf, src_clock);
4314        }
4315    }
4316
4317    type BuiltinFunctionRustInterface<'a> = fn(
4318        &mut InvokeContext<'a>,
4319        u64,
4320        u64,
4321        u64,
4322        u64,
4323        u64,
4324        &mut MemoryMapping,
4325    ) -> Result<u64, Box<dyn std::error::Error>>;
4326
4327    fn call_program_address_common<'a, 'b: 'a>(
4328        invoke_context: &'a mut InvokeContext<'b>,
4329        seeds: &[&[u8]],
4330        program_id: &Pubkey,
4331        overlap_outputs: bool,
4332        syscall: BuiltinFunctionRustInterface<'b>,
4333    ) -> Result<(Pubkey, u8), Error> {
4334        const SEEDS_VA: u64 = 0x100000000;
4335        const PROGRAM_ID_VA: u64 = 0x200000000;
4336        const ADDRESS_VA: u64 = 0x300000000;
4337        const BUMP_SEED_VA: u64 = 0x400000000;
4338        const SEED_VA: u64 = 0x500000000;
4339
4340        let config = Config::default();
4341        let mut address = Pubkey::default();
4342        let mut bump_seed = 0;
4343        let mut regions = vec![
4344            MemoryRegion::new_readonly(bytes_of(program_id), PROGRAM_ID_VA),
4345            MemoryRegion::new_writable(bytes_of_mut(&mut address), ADDRESS_VA),
4346            MemoryRegion::new_writable(bytes_of_mut(&mut bump_seed), BUMP_SEED_VA),
4347        ];
4348
4349        let mut mock_slices = Vec::with_capacity(seeds.len());
4350        for (i, seed) in seeds.iter().enumerate() {
4351            let vm_addr = SEED_VA.saturating_add((i as u64).saturating_mul(0x100000000));
4352            let mock_slice = MockSlice {
4353                vm_addr,
4354                len: seed.len(),
4355            };
4356            mock_slices.push(mock_slice);
4357            regions.push(MemoryRegion::new_readonly(bytes_of_slice(seed), vm_addr));
4358        }
4359        regions.push(MemoryRegion::new_readonly(
4360            bytes_of_slice(&mock_slices),
4361            SEEDS_VA,
4362        ));
4363        let mut memory_mapping = MemoryMapping::new(regions, &config, SBPFVersion::V3).unwrap();
4364
4365        let result = syscall(
4366            invoke_context,
4367            SEEDS_VA,
4368            seeds.len() as u64,
4369            PROGRAM_ID_VA,
4370            ADDRESS_VA,
4371            if overlap_outputs {
4372                ADDRESS_VA
4373            } else {
4374                BUMP_SEED_VA
4375            },
4376            &mut memory_mapping,
4377        );
4378        result.map(|_| (address, bump_seed))
4379    }
4380
4381    fn create_program_address(
4382        invoke_context: &mut InvokeContext,
4383        seeds: &[&[u8]],
4384        address: &Pubkey,
4385    ) -> Result<Pubkey, Error> {
4386        let (address, _) = call_program_address_common(
4387            invoke_context,
4388            seeds,
4389            address,
4390            false,
4391            SyscallCreateProgramAddress::rust,
4392        )?;
4393        Ok(address)
4394    }
4395
4396    fn try_find_program_address(
4397        invoke_context: &mut InvokeContext,
4398        seeds: &[&[u8]],
4399        address: &Pubkey,
4400    ) -> Result<(Pubkey, u8), Error> {
4401        call_program_address_common(
4402            invoke_context,
4403            seeds,
4404            address,
4405            false,
4406            SyscallTryFindProgramAddress::rust,
4407        )
4408    }
4409
4410    #[test]
4411    fn test_set_and_get_return_data() {
4412        const SRC_VA: u64 = 0x100000000;
4413        const DST_VA: u64 = 0x200000000;
4414        const PROGRAM_ID_VA: u64 = 0x300000000;
4415        let data = vec![42; 24];
4416        let mut data_buffer = vec![0; 16];
4417        let mut id_buffer = vec![0; 32];
4418
4419        let config = Config::default();
4420        let mut memory_mapping = MemoryMapping::new(
4421            vec![
4422                MemoryRegion::new_readonly(&data, SRC_VA),
4423                MemoryRegion::new_writable(&mut data_buffer, DST_VA),
4424                MemoryRegion::new_writable(&mut id_buffer, PROGRAM_ID_VA),
4425            ],
4426            &config,
4427            SBPFVersion::V3,
4428        )
4429        .unwrap();
4430
4431        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4432
4433        let result = SyscallSetReturnData::rust(
4434            &mut invoke_context,
4435            SRC_VA,
4436            data.len() as u64,
4437            0,
4438            0,
4439            0,
4440            &mut memory_mapping,
4441        );
4442        assert_eq!(result.unwrap(), 0);
4443
4444        let result = SyscallGetReturnData::rust(
4445            &mut invoke_context,
4446            DST_VA,
4447            data_buffer.len() as u64,
4448            PROGRAM_ID_VA,
4449            0,
4450            0,
4451            &mut memory_mapping,
4452        );
4453        assert_eq!(result.unwrap() as usize, data.len());
4454        assert_eq!(data.get(0..data_buffer.len()).unwrap(), data_buffer);
4455        assert_eq!(id_buffer, program_id.to_bytes());
4456
4457        let result = SyscallGetReturnData::rust(
4458            &mut invoke_context,
4459            PROGRAM_ID_VA,
4460            data_buffer.len() as u64,
4461            PROGRAM_ID_VA,
4462            0,
4463            0,
4464            &mut memory_mapping,
4465        );
4466        assert_matches!(
4467            result,
4468            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4469        );
4470    }
4471
4472    #[test]
4473    fn test_syscall_sol_get_processed_sibling_instruction() {
4474        let transaction_accounts = (0..9)
4475            .map(|_| {
4476                (
4477                    Pubkey::new_unique(),
4478                    AccountSharedData::new(0, 0, &bpf_loader::id()),
4479                )
4480            })
4481            .collect::<Vec<_>>();
4482        let instruction_trace = [1, 2, 3, 2, 2, 3, 4, 3];
4483        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4484        for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
4485            while stack_height
4486                <= invoke_context
4487                    .transaction_context
4488                    .get_instruction_context_stack_height()
4489            {
4490                invoke_context.transaction_context.pop().unwrap();
4491            }
4492            if stack_height
4493                > invoke_context
4494                    .transaction_context
4495                    .get_instruction_context_stack_height()
4496            {
4497                let instruction_accounts = [InstructionAccount {
4498                    index_in_transaction: index_in_trace.saturating_add(1) as IndexOfAccount,
4499                    index_in_caller: 0, // This is incorrect / inconsistent but not required
4500                    index_in_callee: 0,
4501                    is_signer: false,
4502                    is_writable: false,
4503                }];
4504                invoke_context
4505                    .transaction_context
4506                    .get_next_instruction_context()
4507                    .unwrap()
4508                    .configure(&[0], &instruction_accounts, &[index_in_trace as u8]);
4509                invoke_context.transaction_context.push().unwrap();
4510            }
4511        }
4512
4513        let syscall_base_cost = invoke_context.get_compute_budget().syscall_base_cost;
4514
4515        const VM_BASE_ADDRESS: u64 = 0x100000000;
4516        const META_OFFSET: usize = 0;
4517        const PROGRAM_ID_OFFSET: usize =
4518            META_OFFSET + std::mem::size_of::<ProcessedSiblingInstruction>();
4519        const DATA_OFFSET: usize = PROGRAM_ID_OFFSET + std::mem::size_of::<Pubkey>();
4520        const ACCOUNTS_OFFSET: usize = DATA_OFFSET + 0x100;
4521        const END_OFFSET: usize = ACCOUNTS_OFFSET + std::mem::size_of::<AccountInfo>() * 4;
4522        let mut memory = [0u8; END_OFFSET];
4523        let config = Config::default();
4524        let mut memory_mapping = MemoryMapping::new(
4525            vec![MemoryRegion::new_writable(&mut memory, VM_BASE_ADDRESS)],
4526            &config,
4527            SBPFVersion::V3,
4528        )
4529        .unwrap();
4530        let processed_sibling_instruction = translate_type_mut::<ProcessedSiblingInstruction>(
4531            &memory_mapping,
4532            VM_BASE_ADDRESS,
4533            true,
4534        )
4535        .unwrap();
4536        processed_sibling_instruction.data_len = 1;
4537        processed_sibling_instruction.accounts_len = 1;
4538        let program_id = translate_type_mut::<Pubkey>(
4539            &memory_mapping,
4540            VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4541            true,
4542        )
4543        .unwrap();
4544        let data = translate_slice_mut::<u8>(
4545            &memory_mapping,
4546            VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4547            processed_sibling_instruction.data_len,
4548            true,
4549        )
4550        .unwrap();
4551        let accounts = translate_slice_mut::<AccountMeta>(
4552            &memory_mapping,
4553            VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4554            processed_sibling_instruction.accounts_len,
4555            true,
4556        )
4557        .unwrap();
4558
4559        invoke_context.mock_set_remaining(syscall_base_cost);
4560        let result = SyscallGetProcessedSiblingInstruction::rust(
4561            &mut invoke_context,
4562            0,
4563            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4564            VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4565            VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4566            VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4567            &mut memory_mapping,
4568        );
4569        assert_eq!(result.unwrap(), 1);
4570        {
4571            let transaction_context = &invoke_context.transaction_context;
4572            assert_eq!(processed_sibling_instruction.data_len, 1);
4573            assert_eq!(processed_sibling_instruction.accounts_len, 1);
4574            assert_eq!(
4575                program_id,
4576                transaction_context.get_key_of_account_at_index(0).unwrap(),
4577            );
4578            assert_eq!(data, &[5]);
4579            assert_eq!(
4580                accounts,
4581                &[AccountMeta {
4582                    pubkey: *transaction_context.get_key_of_account_at_index(6).unwrap(),
4583                    is_signer: false,
4584                    is_writable: false
4585                }]
4586            );
4587        }
4588
4589        invoke_context.mock_set_remaining(syscall_base_cost);
4590        let result = SyscallGetProcessedSiblingInstruction::rust(
4591            &mut invoke_context,
4592            1,
4593            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4594            VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
4595            VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
4596            VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
4597            &mut memory_mapping,
4598        );
4599        assert_eq!(result.unwrap(), 0);
4600
4601        invoke_context.mock_set_remaining(syscall_base_cost);
4602        let result = SyscallGetProcessedSiblingInstruction::rust(
4603            &mut invoke_context,
4604            0,
4605            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4606            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4607            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4608            VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
4609            &mut memory_mapping,
4610        );
4611        assert_matches!(
4612            result,
4613            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4614        );
4615    }
4616
4617    #[test]
4618    fn test_create_program_address() {
4619        // These tests duplicate the direct tests in clone_solana_program::pubkey
4620
4621        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4622        let address = bpf_loader_upgradeable::id();
4623
4624        let exceeded_seed = &[127; MAX_SEED_LEN + 1];
4625        assert_matches!(
4626            create_program_address(&mut invoke_context, &[exceeded_seed], &address),
4627            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4628        );
4629        assert_matches!(
4630            create_program_address(
4631                &mut invoke_context,
4632                &[b"short_seed", exceeded_seed],
4633                &address,
4634            ),
4635            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4636        );
4637        let max_seed = &[0; MAX_SEED_LEN];
4638        assert!(create_program_address(&mut invoke_context, &[max_seed], &address).is_ok());
4639        let exceeded_seeds: &[&[u8]] = &[
4640            &[1],
4641            &[2],
4642            &[3],
4643            &[4],
4644            &[5],
4645            &[6],
4646            &[7],
4647            &[8],
4648            &[9],
4649            &[10],
4650            &[11],
4651            &[12],
4652            &[13],
4653            &[14],
4654            &[15],
4655            &[16],
4656        ];
4657        assert!(create_program_address(&mut invoke_context, exceeded_seeds, &address).is_ok());
4658        let max_seeds: &[&[u8]] = &[
4659            &[1],
4660            &[2],
4661            &[3],
4662            &[4],
4663            &[5],
4664            &[6],
4665            &[7],
4666            &[8],
4667            &[9],
4668            &[10],
4669            &[11],
4670            &[12],
4671            &[13],
4672            &[14],
4673            &[15],
4674            &[16],
4675            &[17],
4676        ];
4677        assert_matches!(
4678            create_program_address(&mut invoke_context, max_seeds, &address),
4679            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4680        );
4681        assert_eq!(
4682            create_program_address(&mut invoke_context, &[b"", &[1]], &address).unwrap(),
4683            "BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
4684                .parse()
4685                .unwrap(),
4686        );
4687        assert_eq!(
4688            create_program_address(&mut invoke_context, &["☉".as_ref(), &[0]], &address).unwrap(),
4689            "13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
4690                .parse()
4691                .unwrap(),
4692        );
4693        assert_eq!(
4694            create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
4695                .unwrap(),
4696            "2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
4697                .parse()
4698                .unwrap(),
4699        );
4700        let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
4701        assert_eq!(
4702            create_program_address(&mut invoke_context, &[public_key.as_ref(), &[1]], &address)
4703                .unwrap(),
4704            "976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
4705                .parse()
4706                .unwrap(),
4707        );
4708        assert_ne!(
4709            create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
4710                .unwrap(),
4711            create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(),
4712        );
4713        invoke_context.mock_set_remaining(0);
4714        assert_matches!(
4715            create_program_address(&mut invoke_context, &[b"", &[1]], &address),
4716            Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
4717        );
4718    }
4719
4720    #[test]
4721    fn test_find_program_address() {
4722        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4723        let cost = invoke_context
4724            .get_compute_budget()
4725            .create_program_address_units;
4726        let address = bpf_loader_upgradeable::id();
4727        let max_tries = 256; // one per seed
4728
4729        for _ in 0..1_000 {
4730            let address = Pubkey::new_unique();
4731            invoke_context.mock_set_remaining(cost * max_tries);
4732            let (found_address, bump_seed) =
4733                try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address)
4734                    .unwrap();
4735            assert_eq!(
4736                found_address,
4737                create_program_address(
4738                    &mut invoke_context,
4739                    &[b"Lil'", b"Bits", &[bump_seed]],
4740                    &address,
4741                )
4742                .unwrap()
4743            );
4744        }
4745
4746        let seeds: &[&[u8]] = &[b""];
4747        invoke_context.mock_set_remaining(cost * max_tries);
4748        let (_, bump_seed) =
4749            try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
4750        invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64));
4751        try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
4752        invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
4753        assert_matches!(
4754            try_find_program_address(&mut invoke_context, seeds, &address),
4755            Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
4756        );
4757
4758        let exceeded_seed = &[127; MAX_SEED_LEN + 1];
4759        invoke_context.mock_set_remaining(cost * (max_tries - 1));
4760        assert_matches!(
4761            try_find_program_address(&mut invoke_context, &[exceeded_seed], &address),
4762            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4763        );
4764        let exceeded_seeds: &[&[u8]] = &[
4765            &[1],
4766            &[2],
4767            &[3],
4768            &[4],
4769            &[5],
4770            &[6],
4771            &[7],
4772            &[8],
4773            &[9],
4774            &[10],
4775            &[11],
4776            &[12],
4777            &[13],
4778            &[14],
4779            &[15],
4780            &[16],
4781            &[17],
4782        ];
4783        invoke_context.mock_set_remaining(cost * (max_tries - 1));
4784        assert_matches!(
4785            try_find_program_address(&mut invoke_context, exceeded_seeds, &address),
4786            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
4787        );
4788
4789        assert_matches!(
4790            call_program_address_common(
4791                &mut invoke_context,
4792                seeds,
4793                &address,
4794                true,
4795                SyscallTryFindProgramAddress::rust,
4796            ),
4797            Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
4798        );
4799    }
4800
4801    #[test]
4802    fn test_syscall_big_mod_exp() {
4803        let config = Config::default();
4804        prepare_mockup!(invoke_context, program_id, bpf_loader::id());
4805
4806        const VADDR_PARAMS: u64 = 0x100000000;
4807        const MAX_LEN: u64 = 512;
4808        const INV_LEN: u64 = MAX_LEN + 1;
4809        let data: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4810        const VADDR_DATA: u64 = 0x200000000;
4811
4812        let mut data_out: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
4813        const VADDR_OUT: u64 = 0x300000000;
4814
4815        // Test that SyscallBigModExp succeeds with the maximum param size
4816        {
4817            let params_max_len = BigModExpParams {
4818                base: VADDR_DATA as *const u8,
4819                base_len: MAX_LEN,
4820                exponent: VADDR_DATA as *const u8,
4821                exponent_len: MAX_LEN,
4822                modulus: VADDR_DATA as *const u8,
4823                modulus_len: MAX_LEN,
4824            };
4825
4826            let mut memory_mapping = MemoryMapping::new(
4827                vec![
4828                    MemoryRegion::new_readonly(bytes_of(&params_max_len), VADDR_PARAMS),
4829                    MemoryRegion::new_readonly(&data, VADDR_DATA),
4830                    MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4831                ],
4832                &config,
4833                SBPFVersion::V3,
4834            )
4835            .unwrap();
4836
4837            let budget = invoke_context.get_compute_budget();
4838            invoke_context.mock_set_remaining(
4839                budget.syscall_base_cost
4840                    + (MAX_LEN * MAX_LEN) / budget.big_modular_exponentiation_cost_divisor
4841                    + budget.big_modular_exponentiation_base_cost,
4842            );
4843
4844            let result = SyscallBigModExp::rust(
4845                &mut invoke_context,
4846                VADDR_PARAMS,
4847                VADDR_OUT,
4848                0,
4849                0,
4850                0,
4851                &mut memory_mapping,
4852            );
4853
4854            assert_eq!(result.unwrap(), 0);
4855        }
4856
4857        // Test that SyscallBigModExp fails when the maximum param size is exceeded
4858        {
4859            let params_inv_len = BigModExpParams {
4860                base: VADDR_DATA as *const u8,
4861                base_len: INV_LEN,
4862                exponent: VADDR_DATA as *const u8,
4863                exponent_len: INV_LEN,
4864                modulus: VADDR_DATA as *const u8,
4865                modulus_len: INV_LEN,
4866            };
4867
4868            let mut memory_mapping = MemoryMapping::new(
4869                vec![
4870                    MemoryRegion::new_readonly(bytes_of(&params_inv_len), VADDR_PARAMS),
4871                    MemoryRegion::new_readonly(&data, VADDR_DATA),
4872                    MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
4873                ],
4874                &config,
4875                SBPFVersion::V3,
4876            )
4877            .unwrap();
4878
4879            let budget = invoke_context.get_compute_budget();
4880            invoke_context.mock_set_remaining(
4881                budget.syscall_base_cost
4882                    + (INV_LEN * INV_LEN) / budget.big_modular_exponentiation_cost_divisor
4883                    + budget.big_modular_exponentiation_base_cost,
4884            );
4885
4886            let result = SyscallBigModExp::rust(
4887                &mut invoke_context,
4888                VADDR_PARAMS,
4889                VADDR_OUT,
4890                0,
4891                0,
4892                0,
4893                &mut memory_mapping,
4894            );
4895
4896            assert_matches!(
4897                result,
4898                Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::InvalidLength
4899            );
4900        }
4901    }
4902
4903    #[test]
4904    fn test_syscall_get_epoch_stake_total_stake() {
4905        let config = Config::default();
4906        let mut compute_budget = ComputeBudget::default();
4907        let sysvar_cache = Arc::<SysvarCache>::default();
4908
4909        let expected_total_stake = 200_000_000_000_000u64;
4910        // Compute units, as specified by SIMD-0133.
4911        // cu = syscall_base_cost
4912        let expected_cus = compute_budget.syscall_base_cost;
4913
4914        // Set the compute budget to the expected CUs to ensure the syscall
4915        // doesn't exceed the expected usage.
4916        compute_budget.compute_unit_limit = expected_cus;
4917
4918        with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4919        invoke_context.environment_config = EnvironmentConfig::new(
4920            Hash::default(),
4921            0,
4922            expected_total_stake,
4923            &|_| 0, // Vote accounts are not needed for this test.
4924            Arc::<FeatureSet>::default(),
4925            &sysvar_cache,
4926        );
4927
4928        let null_pointer_var = std::ptr::null::<Pubkey>() as u64;
4929
4930        let mut memory_mapping = MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap();
4931
4932        let result = SyscallGetEpochStake::rust(
4933            &mut invoke_context,
4934            null_pointer_var,
4935            0,
4936            0,
4937            0,
4938            0,
4939            &mut memory_mapping,
4940        )
4941        .unwrap();
4942
4943        assert_eq!(result, expected_total_stake);
4944    }
4945
4946    #[test]
4947    fn test_syscall_get_epoch_stake_vote_account_stake() {
4948        let config = Config::default();
4949        let mut compute_budget = ComputeBudget::default();
4950        let sysvar_cache = Arc::<SysvarCache>::default();
4951
4952        let expected_epoch_stake = 55_000_000_000u64;
4953        // Compute units, as specified by SIMD-0133.
4954        // cu = syscall_base_cost
4955        //     + floor(32/cpi_bytes_per_unit)
4956        //     + mem_op_base_cost
4957        let expected_cus = compute_budget.syscall_base_cost
4958            + (PUBKEY_BYTES as u64) / compute_budget.cpi_bytes_per_unit
4959            + compute_budget.mem_op_base_cost;
4960
4961        // Set the compute budget to the expected CUs to ensure the syscall
4962        // doesn't exceed the expected usage.
4963        compute_budget.compute_unit_limit = expected_cus;
4964
4965        let vote_address = Pubkey::new_unique();
4966        with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
4967        let callback = |pubkey: &Pubkey| {
4968            if *pubkey == vote_address {
4969                expected_epoch_stake
4970            } else {
4971                0
4972            }
4973        };
4974        invoke_context.environment_config = EnvironmentConfig::new(
4975            Hash::default(),
4976            0,
4977            0, // Total stake is not needed for this test.
4978            &callback,
4979            Arc::<FeatureSet>::default(),
4980            &sysvar_cache,
4981        );
4982
4983        {
4984            // The syscall aborts the virtual machine if not all bytes in VM
4985            // memory range `[vote_addr, vote_addr + 32)` are readable.
4986            let vote_address_var = 0x100000000;
4987
4988            let mut memory_mapping = MemoryMapping::new(
4989                vec![
4990                    // Invalid read-only memory region.
4991                    MemoryRegion::new_readonly(&[2; 31], vote_address_var),
4992                ],
4993                &config,
4994                SBPFVersion::V3,
4995            )
4996            .unwrap();
4997
4998            let result = SyscallGetEpochStake::rust(
4999                &mut invoke_context,
5000                vote_address_var,
5001                0,
5002                0,
5003                0,
5004                0,
5005                &mut memory_mapping,
5006            );
5007
5008            assert_access_violation!(result, vote_address_var, 32);
5009        }
5010
5011        invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);
5012        {
5013            // Otherwise, the syscall returns a `u64` integer representing the
5014            // total active stake delegated to the vote account at the provided
5015            // address.
5016            let vote_address_var = 0x100000000;
5017
5018            let mut memory_mapping = MemoryMapping::new(
5019                vec![MemoryRegion::new_readonly(
5020                    bytes_of(&vote_address),
5021                    vote_address_var,
5022                )],
5023                &config,
5024                SBPFVersion::V3,
5025            )
5026            .unwrap();
5027
5028            let result = SyscallGetEpochStake::rust(
5029                &mut invoke_context,
5030                vote_address_var,
5031                0,
5032                0,
5033                0,
5034                0,
5035                &mut memory_mapping,
5036            )
5037            .unwrap();
5038
5039            assert_eq!(result, expected_epoch_stake);
5040        }
5041
5042        invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);
5043        {
5044            // If the provided vote address corresponds to an account that is
5045            // not a vote account or does not exist, the syscall will write
5046            // `0` for active stake.
5047            let vote_address_var = 0x100000000;
5048            let not_a_vote_address = Pubkey::new_unique(); // Not a vote account.
5049
5050            let mut memory_mapping = MemoryMapping::new(
5051                vec![MemoryRegion::new_readonly(
5052                    bytes_of(&not_a_vote_address),
5053                    vote_address_var,
5054                )],
5055                &config,
5056                SBPFVersion::V3,
5057            )
5058            .unwrap();
5059
5060            let result = SyscallGetEpochStake::rust(
5061                &mut invoke_context,
5062                vote_address_var,
5063                0,
5064                0,
5065                0,
5066                0,
5067                &mut memory_mapping,
5068            )
5069            .unwrap();
5070
5071            assert_eq!(result, 0); // `0` for active stake.
5072        }
5073    }
5074
5075    #[test]
5076    fn test_check_type_assumptions() {
5077        check_type_assumptions();
5078    }
5079
5080    fn bytes_of<T>(val: &T) -> &[u8] {
5081        let size = mem::size_of::<T>();
5082        unsafe { slice::from_raw_parts(std::slice::from_ref(val).as_ptr().cast(), size) }
5083    }
5084
5085    fn bytes_of_mut<T>(val: &mut T) -> &mut [u8] {
5086        let size = mem::size_of::<T>();
5087        unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) }
5088    }
5089
5090    fn bytes_of_slice<T>(val: &[T]) -> &[u8] {
5091        let size = val.len().wrapping_mul(mem::size_of::<T>());
5092        unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) }
5093    }
5094
5095    fn bytes_of_slice_mut<T>(val: &mut [T]) -> &mut [u8] {
5096        let size = val.len().wrapping_mul(mem::size_of::<T>());
5097        unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) }
5098    }
5099
5100    #[test]
5101    fn test_address_is_aligned() {
5102        for address in 0..std::mem::size_of::<u64>() {
5103            assert_eq!(address_is_aligned::<u64>(address as u64), address == 0);
5104        }
5105    }
5106}