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