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