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