1#[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
80const MAX_SIGNERS: usize = 16;
82
83#[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#[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 pub fn resize(&mut self, len: u64) {
276 self.len = len;
277 }
278
279 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 let mut result = BuiltinProgram::new_loader(config);
323
324 for (_key, (name, value)) in from.get_function_registry().iter() {
325 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 };
388 let mut result = BuiltinProgram::new_loader(config);
389
390 result.register_function("abort", SyscallAbort::vm)?;
392
393 result.register_function("rlo_panic_", SyscallPanic::vm)?;
395 result.register_function("sol_panic_", SyscallPanic::vm)?; result.register_function("rlo_log_", SyscallLog::vm)?;
399 result.register_function("sol_log_", SyscallLog::vm)?; result.register_function("rlo_log_64_", SyscallLogU64::vm)?;
401 result.register_function("sol_log_64_", SyscallLogU64::vm)?; result.register_function("rlo_log_pubkey", SyscallLogPubkey::vm)?;
403 result.register_function("sol_log_pubkey", SyscallLogPubkey::vm)?; result.register_function("rlo_log_compute_units_", SyscallLogBpfComputeUnits::vm)?;
405 result.register_function("sol_log_compute_units_", SyscallLogBpfComputeUnits::vm)?; 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 )?; 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 )?; result.register_function("rlo_sha256", SyscallHash::vm::<Sha256Hasher>)?;
427 result.register_function("sol_sha256", SyscallHash::vm::<Sha256Hasher>)?; result.register_function("rlo_keccak256", SyscallHash::vm::<Keccak256Hasher>)?;
431 result.register_function("sol_keccak256", SyscallHash::vm::<Keccak256Hasher>)?; result.register_function("rlo_secp256k1_recover", SyscallSecp256k1Recover::vm)?;
435 result.register_function("sol_secp256k1_recover", SyscallSecp256k1Recover::vm)?; 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 )?; 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 )?; 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 )?; 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 )?; result.register_function("rlo_get_clock_sysvar", SyscallGetClockSysvar::vm)?;
491 result.register_function("sol_get_clock_sysvar", SyscallGetClockSysvar::vm)?; 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 )?; result.register_function("rlo_get_rent_sysvar", SyscallGetRentSysvar::vm)?;
501 result.register_function("sol_get_rent_sysvar", SyscallGetRentSysvar::vm)?; result.register_function("rlo_get_random_seed", SyscallGetRandomSeed::vm)?;
503
504 result.register_function("rlo_memcpy_", SyscallMemcpy::vm)?;
506 result.register_function("sol_memcpy_", SyscallMemcpy::vm)?; result.register_function("rlo_memmove_", SyscallMemmove::vm)?;
508 result.register_function("sol_memmove_", SyscallMemmove::vm)?; result.register_function("rlo_memset_", SyscallMemset::vm)?;
510 result.register_function("sol_memset_", SyscallMemset::vm)?; result.register_function("rlo_memcmp_", SyscallMemcmp::vm)?;
512 result.register_function("sol_memcmp_", SyscallMemcmp::vm)?; 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 )?; result.register_function("rlo_get_stack_height", SyscallGetStackHeight::vm)?;
526 result.register_function("sol_get_stack_height", SyscallGetStackHeight::vm)?; result.register_function("rlo_set_return_data", SyscallSetReturnData::vm)?;
530 result.register_function("sol_set_return_data", SyscallSetReturnData::vm)?; result.register_function("rlo_get_return_data", SyscallGetReturnData::vm)?;
532 result.register_function("sol_get_return_data", SyscallGetReturnData::vm)?; result.register_function("rlo_invoke_signed_c", SyscallInvokeSignedC::vm)?;
536 result.register_function("sol_invoke_signed_c", SyscallInvokeSignedC::vm)?; result.register_function("rlo_invoke_signed_rust", SyscallInvokeSignedRust::vm)?;
538 result.register_function("sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)?; 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 )?; 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 )?; 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 )?; 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 )?; 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 )?; 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 )?; 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 )?; result.register_function("rlo_log_data", SyscallLogData::vm)?;
640 result.register_function("sol_log_data", SyscallLogData::vm)?; 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, 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 };
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
828fn 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 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 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 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 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 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 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 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 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 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 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 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 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 Ok(return_data.len() as u64)
1628 }
1629);
1630
1631declare_builtin_function!(
1632 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(); 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, 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, 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, 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 {
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 {
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 {
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 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, 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, 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, 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 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, 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, 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 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, 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, 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 {
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 {
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 assert!(are_bytes_equal(
3723 &epochschedule_from_buf.clone(),
3724 &clean_epochschedule
3725 ));
3726 }
3727
3728 {
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 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 with_mock_invoke_context!(
3826 invoke_context,
3827 transaction_context,
3828 Vec::<(Pubkey, StoredAccount)>::new()
3829 );
3830
3831 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 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 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 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 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 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, 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 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; 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 {
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(¶ms_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 {
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(¶ms_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}