Skip to main content

rialo_s_bpf_loader_program/syscalls/
mod.rs

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