agave_syscalls/
lib.rs

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