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