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