solana_bpf_loader_program/syscalls/
mod.rs

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