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