solana/programs/bpf_loader/src/syscalls/mod.rs

4146 lines
142 KiB
Rust
Raw Normal View History

2022-08-06 07:31:47 -07:00
pub use self::{
cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust},
logging::{
SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64,
},
mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
sysvar::{
SyscallGetClockSysvar, SyscallGetEpochRewardsSysvar, SyscallGetEpochScheduleSysvar,
SyscallGetFeesSysvar, SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
2022-08-06 07:31:47 -07:00
},
};
2021-07-29 10:48:14 -07:00
#[allow(deprecated)]
use {
solana_program_runtime::{
compute_budget::ComputeBudget, ic_logger_msg, ic_msg, invoke_context::InvokeContext,
stable_log, timings::ExecuteTimings,
},
solana_rbpf::{
memory_region::{AccessType, MemoryMapping},
vm::{BuiltinProgram, Config, ProgramResult, PROGRAM_ENVIRONMENT_KEY_SHIFT},
},
solana_sdk::{
account::{ReadableAccount, WritableAccount},
account_info::AccountInfo,
alt_bn128::prelude::{
alt_bn128_addition, alt_bn128_multiplication, alt_bn128_pairing, AltBn128Error,
ALT_BN128_ADDITION_OUTPUT_LEN, ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
ALT_BN128_PAIRING_ELEMENT_LEN, ALT_BN128_PAIRING_OUTPUT_LEN,
},
big_mod_exp::{big_mod_exp, BigModExpParams},
blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS},
Account data direct mapping (#28053) * AccountSharedData: make data_mut() private This ensures that the inner Vec is never handed out. This is in preparation of enforcing that the capacity of the inner vec never shrinks, which is required for direct mapping. * Adds the feature bpf_account_data_direct_mapping. * Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified. * WIP: Memory regions for each instruction account in create_vm(). * Fix serialization benches, run both copy and !copy variants * rbpf-cli: fix build * BorrowedAccount: ensure that account capacity is never reduced Accounts can be directly mapped in address space. Their capacity can't be reduced mid transaction as that would create holes in vm address space that point to invalid host memory. * bpf_load: run serialization tests for both copy and !copy account data * bpf_loader: add Serializer::write_account * fix lints * BorrowedAccount: make_data_mut is host only * Fix unused import warning * Fix lints * cpi: add explicit direct_mapping arg to update_(callee|caller)_account * cpi: rename account_data_or_only_realloc_padding to serialized_data * cpi: add CallerAccount::original_data_len comment * cpi: add update_callee_account direct_mapping test * cpi: add test_update_caller_account_data_direct_mapping and fix bug We used to have a bug in zeroing data when shrinking account, where we zeroed the spare account capacity but not the realloc padding. * cpi: add tests for mutated readonly accounts * cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on * cpi: update_caller_account: ensure that account capacity is always enough Introduce a better way to ensure that account capacity never goes below what might be mapped in memory regions. * cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut() Before we were using BorrowedAccount::get_data_mut() to get the base pointer to the account data, then we were slicing the spare capacity from it. Calling get_data_mut() doesn't work if an account has been closed tho, since the current program doesn't own the account anymore and therefore get_data_mut() errors out. * bpf_loader: fix same lint for the umpteenth time * bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations * programs/sbf: realloc: add test for large write after realloc Add a test that after a realloc does a large write that spans the original account length and the realloc area. This ensures that memory mapping works correctly across the boundary. * programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off By default test banks test with all features on. This ensures we keep testing the existing code until the new feature is enabled. * bpf_loader: tweak memcmp syscall Split the actual memcmp code in a separate function. Remove check indexing the slices since the slices are guaranteed to have the correct length by construction. * bpf_loader: tweak the memset syscall Use slice::fill, which is effectively memset. * bpf_loader: syscalls: update mem syscalls to work with non contiguous memory With direct mapping enabled, accounts can now span multiple memory regions. * fix lint, rebase mem_ops * Implement CoW for writable accounts * Fix CI * Move CoW to the MemoryMapping level * Update after rbpf API change * Fix merge screwup * Add create_vm macro. Fix benches. * cpi: simplify update_caller_account Simplify the logic to update a caller's memory region when a callee causes an account data pointer to change (eg during CoW) * benches/bpf_loader: move serialization out of create_vm bench * benches/bpf_loader: don't copy accounts when direct mapping is on * Fix review nits * bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new When starting at u64::MAX, the chunk iterator would always return the empty sequence (None on the first next()) call, instead of returning a memory access violation. Use checked instead of saturating arithmetic to detect the condition and error out. This commit also adds more tests around boundary conditions. * Fix loader-v3 tests: data_mut => data_as_mut_slice * Fix CI * bpf_loader: fix tuner bench: account must be writable With direct mapping on, invalid writes are caught early meaning the tuner would fail on the first store and not consume the whole budget like the benchmark expects. --------- Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
feature_set::bpf_account_data_direct_mapping,
feature_set::FeatureSet,
feature_set::{
self, blake3_syscall_enabled, curve25519_syscall_enabled,
disable_cpi_setting_executable_and_rent_epoch, disable_deploy_of_alloc_free_syscall,
disable_fees_sysvar, enable_alt_bn128_syscall, enable_big_mod_exp_syscall,
enable_early_verification_of_account_modifications, enable_partitioned_epoch_reward,
enable_poseidon_syscall, error_on_syscall_bpf_function_hash_collisions,
last_restart_slot_sysvar, libsecp256k1_0_5_upgrade_enabled, reject_callx_r10,
stop_sibling_instruction_search_at_parent, stop_truncating_strings_in_syscalls,
switch_to_new_elf_parser,
},
hash::{Hasher, HASH_BYTES},
instruction::{
AccountMeta, InstructionError, ProcessedSiblingInstruction,
TRANSACTION_LEVEL_STACK_HEIGHT,
},
keccak, native_loader, poseidon,
precompiles::is_precompile,
program::MAX_RETURN_DATA,
program_stubs::is_nonoverlapping,
pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN},
secp256k1_recover::{
Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
},
sysvar::{Sysvar, SysvarId},
transaction_context::{IndexOfAccount, InstructionAccount},
},
std::{
alloc::Layout,
mem::{align_of, size_of},
slice::from_raw_parts_mut,
str::{from_utf8, Utf8Error},
sync::Arc,
},
thiserror::Error as ThisError,
2020-01-09 23:58:13 -08:00
};
mod cpi;
mod logging;
mod mem_ops;
mod sysvar;
2020-12-09 02:14:53 -08:00
/// Maximum signers
pub const MAX_SIGNERS: usize = 16;
/// Error definitions
#[derive(Debug, ThisError, PartialEq, Eq)]
pub enum SyscallError {
#[error("{0}: {1:?}")]
InvalidString(Utf8Error, Vec<u8>),
#[error("SBF program panicked")]
Abort,
#[error("SBF program Panicked in {0} at {1}:{2}")]
Panic(String, u64, u64),
#[error("Cannot borrow invoke context")]
2020-04-28 14:33:56 -07:00
InvokeContextBorrowFailed,
#[error("Malformed signer seed: {0}: {1:?}")]
2020-04-28 14:33:56 -07:00
MalformedSignerSeed(Utf8Error, Vec<u8>),
#[error("Could not create program address with signer seeds: {0}")]
BadSeeds(PubkeyError),
2021-02-01 11:40:49 -08:00
#[error("Program {0} not supported by inner instructions")]
ProgramNotSupported(Pubkey),
#[error("Unaligned pointer")]
UnalignedPointer,
2020-12-09 02:14:53 -08:00
#[error("Too many signers")]
TooManySigners,
2021-02-01 11:40:49 -08:00
#[error("Instruction passed to inner instruction is too large ({0} > {1})")]
InstructionTooLarge(usize, usize),
#[error("Too many accounts passed to inner instruction")]
TooManyAccounts,
2021-06-01 15:33:17 -07:00
#[error("Overlapping copy")]
CopyOverlapping,
#[error("Return data too large ({0} > {1})")]
ReturnDataTooLarge(u64, u64),
#[error("Hashing too many sequences")]
TooManySlices,
2022-03-10 11:48:33 -08:00
#[error("InvalidLength")]
InvalidLength,
#[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")]
MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 },
#[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")]
MaxInstructionAccountsExceeded {
num_accounts: u64,
max_accounts: u64,
},
#[error("Invoked an instruction with too many account info's ({num_account_infos} > {max_account_infos})")]
MaxInstructionAccountInfosExceeded {
num_account_infos: u64,
max_account_infos: u64,
},
#[error("InvalidAttribute")]
InvalidAttribute,
direct mapping: misc fixes (#32649) * transaction_context: update make_data_mut comment * bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from* We now have a way to provide CallerAccount with trusted values coming from our internal serialization code and not from untrusted vm space * bpf_loader: direct_mapping: enforce account info pointers to be immutable When direct mapping is enabled, we might need to update account data memory regions across CPI calls. Since the only way we have to retrieve the regions is based on their vm addresses, we enforce vm addresses to be stable. Accounts can still be mutated and resized of course, but it must be done in place. This also locks all other AccountInfo pointers, since there's no legitimate reason to make them point to anything else. * bpf_loader: cpi: access ref_to_len_in_vm through VmValue Direct mapping needs to translate vm values at each access since permissions of the underlying memory might have changed. * direct mapping: improve memory permission tracking across CPI calls Ensure that the data and realloc regions of an account always track the account's permissions. In order to do this, we also need to split realloc regions in their own self contained regions, where before we had: [account fields][account data][account realloc + more account fields + next account fields][next account data][...] we now have: [account fields][account data][account realloc][more account fields + next account fields][next account data][...] Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE* Additionally when direct mapping is on, we must update all perms at once before doing account data updates. Otherwise, updating an account might write into another account whose perms we haven't updated yet. Tested in TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE. * bpf_loader: serialization: address review comment don't return vm_addr from push_account_region * bpf_loader: rename push_account_region to push_account_data_region * cpi: fix slow edge case zeroing extra account capacity after shrinking an account When returning from CPI we need to zero all the account memory up to the original length only if we know we're potentially dealing with uninitialized memory. When we know that the spare capacity has deterministic content, we only need to zero new_len..prev_len. This fixes a slow edge case that was triggerable by the following scenario: - load a large account (say 10MB) into the vm - shrink to 10 bytes - would memset 10..10MB - shrink to 9 bytes - would memset 9..10MB - shrink to 8 bytes - would memset 8..10MB - ... Now instead in the scenario above the following will happen: - load a large account (say 10MB) into the vm - shrink to 10 bytes - memsets 10..10MB - shrink to 9 bytes - memsets 9..10 - shrink to 8 bytes - memset 8..9 - ... * bpf_loader: add account_data_region_memory_state helper Shared between serialization and CPI to figure out the MemoryState of an account. * cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it, essentially letting callees directly mutate callers memory. * bpf_loader: direct_mapping: map AccessViolation -> InstructionError Return the proper ReadonlyDataModified / ExecutableDataModified / ExternalAccountDataModified depending on where the violation occurs * bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
#[error("Invalid pointer")]
InvalidPointer,
#[error("Arithmetic overflow")]
ArithmeticOverflow,
}
type Error = Box<dyn std::error::Error>;
fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), Error> {
invoke_context.consume_checked(amount)?;
Ok(())
}
macro_rules! register_feature_gated_function {
($result:expr, $is_feature_active:expr, $name:expr, $call:expr $(,)?) => {
if $is_feature_active {
$result.register_function($name, $call)
} else {
Ok(())
}
};
}
pub fn create_program_runtime_environment_v1<'a>(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
reject_deployment_of_broken_elfs: bool,
debugging_features: bool,
) -> Result<BuiltinProgram<InvokeContext<'a>>, Error> {
2023-07-05 10:46:21 -07:00
let enable_alt_bn128_syscall = feature_set.is_active(&enable_alt_bn128_syscall::id());
let enable_big_mod_exp_syscall = feature_set.is_active(&enable_big_mod_exp_syscall::id());
let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id());
let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id());
let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id());
let epoch_rewards_syscall_enabled =
feature_set.is_active(&enable_partitioned_epoch_reward::id());
let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
&& feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id());
let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id());
let enable_poseidon_syscall = feature_set.is_active(&enable_poseidon_syscall::id());
// !!! ATTENTION !!!
// When adding new features for RBPF here,
// also add them to `Bank::apply_builtin_program_feature_transitions()`.
2023-07-05 10:46:21 -07:00
use rand::Rng;
let config = Config {
max_call_depth: compute_budget.max_call_depth,
stack_frame_size: compute_budget.stack_frame_size,
enable_address_translation: true,
enable_stack_frame_gaps: true,
instruction_meter_checkpoint_distance: 10000,
enable_instruction_meter: true,
enable_instruction_tracing: debugging_features,
enable_symbol_and_section_labels: debugging_features,
reject_broken_elfs: reject_deployment_of_broken_elfs,
noop_instruction_rate: 256,
sanitize_user_provided_values: true,
runtime_environment_key: rand::thread_rng()
.gen::<i32>()
.checked_shr(PROGRAM_ENVIRONMENT_KEY_SHIFT)
.unwrap_or(0),
external_internal_function_hash_collision: feature_set
.is_active(&error_on_syscall_bpf_function_hash_collisions::id()),
reject_callx_r10: feature_set.is_active(&reject_callx_r10::id()),
2023-07-05 10:46:21 -07:00
enable_sbpf_v1: true,
enable_sbpf_v2: false,
optimize_rodata: false,
new_elf_parser: feature_set.is_active(&switch_to_new_elf_parser::id()),
Account data direct mapping (#28053) * AccountSharedData: make data_mut() private This ensures that the inner Vec is never handed out. This is in preparation of enforcing that the capacity of the inner vec never shrinks, which is required for direct mapping. * Adds the feature bpf_account_data_direct_mapping. * Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified. * WIP: Memory regions for each instruction account in create_vm(). * Fix serialization benches, run both copy and !copy variants * rbpf-cli: fix build * BorrowedAccount: ensure that account capacity is never reduced Accounts can be directly mapped in address space. Their capacity can't be reduced mid transaction as that would create holes in vm address space that point to invalid host memory. * bpf_load: run serialization tests for both copy and !copy account data * bpf_loader: add Serializer::write_account * fix lints * BorrowedAccount: make_data_mut is host only * Fix unused import warning * Fix lints * cpi: add explicit direct_mapping arg to update_(callee|caller)_account * cpi: rename account_data_or_only_realloc_padding to serialized_data * cpi: add CallerAccount::original_data_len comment * cpi: add update_callee_account direct_mapping test * cpi: add test_update_caller_account_data_direct_mapping and fix bug We used to have a bug in zeroing data when shrinking account, where we zeroed the spare account capacity but not the realloc padding. * cpi: add tests for mutated readonly accounts * cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on * cpi: update_caller_account: ensure that account capacity is always enough Introduce a better way to ensure that account capacity never goes below what might be mapped in memory regions. * cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut() Before we were using BorrowedAccount::get_data_mut() to get the base pointer to the account data, then we were slicing the spare capacity from it. Calling get_data_mut() doesn't work if an account has been closed tho, since the current program doesn't own the account anymore and therefore get_data_mut() errors out. * bpf_loader: fix same lint for the umpteenth time * bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations * programs/sbf: realloc: add test for large write after realloc Add a test that after a realloc does a large write that spans the original account length and the realloc area. This ensures that memory mapping works correctly across the boundary. * programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off By default test banks test with all features on. This ensures we keep testing the existing code until the new feature is enabled. * bpf_loader: tweak memcmp syscall Split the actual memcmp code in a separate function. Remove check indexing the slices since the slices are guaranteed to have the correct length by construction. * bpf_loader: tweak the memset syscall Use slice::fill, which is effectively memset. * bpf_loader: syscalls: update mem syscalls to work with non contiguous memory With direct mapping enabled, accounts can now span multiple memory regions. * fix lint, rebase mem_ops * Implement CoW for writable accounts * Fix CI * Move CoW to the MemoryMapping level * Update after rbpf API change * Fix merge screwup * Add create_vm macro. Fix benches. * cpi: simplify update_caller_account Simplify the logic to update a caller's memory region when a callee causes an account data pointer to change (eg during CoW) * benches/bpf_loader: move serialization out of create_vm bench * benches/bpf_loader: don't copy accounts when direct mapping is on * Fix review nits * bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new When starting at u64::MAX, the chunk iterator would always return the empty sequence (None on the first next()) call, instead of returning a memory access violation. Use checked instead of saturating arithmetic to detect the condition and error out. This commit also adds more tests around boundary conditions. * Fix loader-v3 tests: data_mut => data_as_mut_slice * Fix CI * bpf_loader: fix tuner bench: account must be writable With direct mapping on, invalid writes are caught early meaning the tuner would fail on the first store and not consume the whole budget like the benchmark expects. --------- Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
aligned_memory_mapping: !feature_set.is_active(&bpf_account_data_direct_mapping::id()),
// Warning, do not use `Config::default()` so that configuration here is explicit.
};
let mut result = BuiltinProgram::new_loader(config);
// Abort
result.register_function(b"abort", SyscallAbort::call)?;
// Panic
result.register_function(b"sol_panic_", SyscallPanic::call)?;
// Logging
result.register_function(b"sol_log_", SyscallLog::call)?;
result.register_function(b"sol_log_64_", SyscallLogU64::call)?;
result.register_function(b"sol_log_compute_units_", SyscallLogBpfComputeUnits::call)?;
result.register_function(b"sol_log_pubkey", SyscallLogPubkey::call)?;
// Program defined addresses (PDA)
result.register_function(
b"sol_create_program_address",
SyscallCreateProgramAddress::call,
)?;
result.register_function(
b"sol_try_find_program_address",
2021-02-18 09:56:11 -08:00
SyscallTryFindProgramAddress::call,
)?;
2021-03-30 12:16:21 -07:00
// Sha256
result.register_function(b"sol_sha256", SyscallSha256::call)?;
// Keccak256
result.register_function(b"sol_keccak256", SyscallKeccak256::call)?;
2021-06-08 11:04:10 -07:00
// Secp256k1 Recover
result.register_function(b"sol_secp256k1_recover", SyscallSecp256k1Recover::call)?;
// Blake3
register_feature_gated_function!(
result,
blake3_syscall_enabled,
b"sol_blake3",
SyscallBlake3::call,
)?;
// Elliptic Curve Operations
register_feature_gated_function!(
result,
curve25519_syscall_enabled,
b"sol_curve_validate_point",
SyscallCurvePointValidation::call,
)?;
register_feature_gated_function!(
result,
curve25519_syscall_enabled,
b"sol_curve_group_op",
SyscallCurveGroupOps::call,
)?;
register_feature_gated_function!(
result,
curve25519_syscall_enabled,
b"sol_curve_multiscalar_mul",
SyscallCurveMultiscalarMultiplication::call,
)?;
// Sysvars
result.register_function(b"sol_get_clock_sysvar", SyscallGetClockSysvar::call)?;
result.register_function(
b"sol_get_epoch_schedule_sysvar",
2021-07-29 15:03:00 -07:00
SyscallGetEpochScheduleSysvar::call,
)?;
register_feature_gated_function!(
result,
!disable_fees_sysvar,
b"sol_get_fees_sysvar",
SyscallGetFeesSysvar::call,
)?;
result.register_function(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?;
2021-04-12 16:04:57 -07:00
register_feature_gated_function!(
result,
last_restart_slot_syscall_enabled,
b"sol_get_last_restart_slot",
SyscallGetLastRestartSlotSysvar::call,
)?;
register_feature_gated_function!(
result,
epoch_rewards_syscall_enabled,
b"sol_get_epoch_rewards_sysvar",
SyscallGetEpochRewardsSysvar::call,
)?;
// Memory ops
result.register_function(b"sol_memcpy_", SyscallMemcpy::call)?;
result.register_function(b"sol_memmove_", SyscallMemmove::call)?;
result.register_function(b"sol_memcmp_", SyscallMemcmp::call)?;
result.register_function(b"sol_memset_", SyscallMemset::call)?;
2021-06-01 15:33:17 -07:00
// Processed sibling instructions
result.register_function(
b"sol_get_processed_sibling_instruction",
SyscallGetProcessedSiblingInstruction::call,
)?;
// Stack height
result.register_function(b"sol_get_stack_height", SyscallGetStackHeight::call)?;
// Return data
result.register_function(b"sol_set_return_data", SyscallSetReturnData::call)?;
result.register_function(b"sol_get_return_data", SyscallGetReturnData::call)?;
// Cross-program invocation
result.register_function(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
result.register_function(b"sol_invoke_signed_rust", SyscallInvokeSignedRust::call)?;
// Memory allocator
register_feature_gated_function!(
result,
!disable_deploy_of_alloc_free_syscall,
b"sol_alloc_free_",
SyscallAllocFree::call,
)?;
// Alt_bn128
register_feature_gated_function!(
result,
enable_alt_bn128_syscall,
b"sol_alt_bn128_group_op",
SyscallAltBn128::call,
)?;
// Big_mod_exp
register_feature_gated_function!(
result,
enable_big_mod_exp_syscall,
b"sol_big_mod_exp",
SyscallBigModExp::call,
)?;
// Poseidon
register_feature_gated_function!(
result,
enable_poseidon_syscall,
b"sol_poseidon",
SyscallPoseidon::call,
)?;
// Log data
result.register_function(b"sol_log_data", SyscallLogData::call)?;
Ok(result)
}
fn address_is_aligned<T>(address: u64) -> bool {
(address as *mut T as usize)
.checked_rem(align_of::<T>())
.map(|rem| rem == 0)
.expect("T to be non-zero aligned")
}
fn translate(
memory_mapping: &MemoryMapping,
access_type: AccessType,
vm_addr: u64,
len: u64,
) -> Result<u64, Error> {
memory_mapping.map(access_type, vm_addr, len, 0).into()
}
fn translate_type_inner<'a, T>(
memory_mapping: &MemoryMapping,
access_type: AccessType,
vm_addr: u64,
check_aligned: bool,
) -> Result<&'a mut T, Error> {
let host_addr = translate(memory_mapping, access_type, vm_addr, size_of::<T>() as u64)?;
if !check_aligned {
Ok(unsafe { std::mem::transmute::<u64, &mut T>(host_addr) })
} else if !address_is_aligned::<T>(host_addr) {
Err(SyscallError::UnalignedPointer.into())
} else {
Ok(unsafe { &mut *(host_addr as *mut T) })
}
}
fn translate_type_mut<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
check_aligned: bool,
) -> Result<&'a mut T, Error> {
translate_type_inner::<T>(memory_mapping, AccessType::Store, vm_addr, check_aligned)
}
fn translate_type<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
check_aligned: bool,
) -> Result<&'a T, Error> {
translate_type_inner::<T>(memory_mapping, AccessType::Load, vm_addr, check_aligned)
2021-09-17 09:46:49 -07:00
.map(|value| &*value)
}
fn translate_slice_inner<'a, T>(
memory_mapping: &MemoryMapping,
access_type: AccessType,
vm_addr: u64,
len: u64,
check_aligned: bool,
check_size: bool,
) -> Result<&'a mut [T], Error> {
if len == 0 {
return Ok(&mut []);
}
let total_size = len.saturating_mul(size_of::<T>() as u64);
if check_size && isize::try_from(total_size).is_err() {
return Err(SyscallError::InvalidLength.into());
}
let host_addr = translate(memory_mapping, access_type, vm_addr, total_size)?;
if check_aligned && !address_is_aligned::<T>(host_addr) {
return Err(SyscallError::UnalignedPointer.into());
}
Ok(unsafe { from_raw_parts_mut(host_addr as *mut T, len as usize) })
}
fn translate_slice_mut<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
check_size: bool,
) -> Result<&'a mut [T], Error> {
translate_slice_inner::<T>(
memory_mapping,
AccessType::Store,
vm_addr,
len,
check_aligned,
check_size,
)
}
fn translate_slice<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
check_size: bool,
) -> Result<&'a [T], Error> {
translate_slice_inner::<T>(
memory_mapping,
AccessType::Load,
vm_addr,
len,
check_aligned,
check_size,
)
.map(|value| &*value)
}
/// Take a virtual pointer to a string (points to SBF VM memory space), translate it
/// pass it to a user-defined work function
fn translate_string_and_do(
memory_mapping: &MemoryMapping,
addr: u64,
len: u64,
check_aligned: bool,
check_size: bool,
stop_truncating_strings_in_syscalls: bool,
work: &mut dyn FnMut(&str) -> Result<u64, Error>,
) -> Result<u64, Error> {
let buf = translate_slice::<u8>(memory_mapping, addr, len, check_aligned, check_size)?;
let msg = if stop_truncating_strings_in_syscalls {
buf
} else {
let i = match buf.iter().position(|byte| *byte == 0) {
Some(i) => i,
None => len as usize,
};
buf.get(..i).ok_or(SyscallError::InvalidLength)?
};
2022-03-10 11:48:33 -08:00
match from_utf8(msg) {
Ok(message) => work(message),
2022-03-10 11:48:33 -08:00
Err(err) => Err(SyscallError::InvalidString(err, msg.to_vec()).into()),
}
}
#[macro_export]
macro_rules! declare_syscall {
($(#[$attr:meta])* $name:ident, $inner_call:item) => {
$(#[$attr])*
pub struct $name {}
impl $name {
$inner_call
pub fn call(
invoke_context: &mut InvokeContext,
arg_a: u64,
arg_b: u64,
arg_c: u64,
arg_d: u64,
arg_e: u64,
memory_mapping: &mut MemoryMapping,
result: &mut ProgramResult,
) {
let converted_result: ProgramResult = Self::inner_call(
invoke_context, arg_a, arg_b, arg_c, arg_d, arg_e, memory_mapping,
).into();
*result = converted_result;
}
}
};
2022-04-11 16:05:09 -07:00
}
declare_syscall!(
/// Abort syscall functions, called when the SBF program calls `abort()`
/// LLVM will insert calls to `abort()` if it detects an untenable situation,
/// `abort()` is not intended to be called explicitly by the program.
/// Causes the SBF program to be halted immediately
SyscallAbort,
fn inner_call(
_invoke_context: &mut InvokeContext,
_arg1: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
_memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
Err(SyscallError::Abort.into())
}
);
declare_syscall!(
/// Panic syscall function, called when the SBF program calls 'sol_panic_()`
/// Causes the SBF program to be halted immediately
SyscallPanic,
fn inner_call(
invoke_context: &mut InvokeContext,
file: u64,
len: u64,
line: u64,
column: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
consume_compute_meter(invoke_context, len)?;
translate_string_and_do(
memory_mapping,
file,
len,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
invoke_context
.feature_set
.is_active(&stop_truncating_strings_in_syscalls::id()),
&mut |string: &str| Err(SyscallError::Panic(string.to_string(), line, column).into()),
)
}
);
declare_syscall!(
/// Dynamic memory allocation syscall called when the SBF program calls
/// `sol_alloc_free_()`. The allocator is expected to allocate/free
/// from/to a given chunk of memory and enforce size restrictions. The
/// memory chunk is given to the allocator during allocator creation and
/// information about that memory (start address and size) is passed
/// to the VM to use for enforcement.
SyscallAllocFree,
fn inner_call(
invoke_context: &mut InvokeContext,
size: u64,
free_addr: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
_memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
2022-04-11 16:05:09 -07:00
let align = if invoke_context.get_check_aligned() {
2021-11-30 10:41:15 -08:00
BPF_ALIGN_OF_U128
2020-08-24 13:21:34 -07:00
} else {
align_of::<u8>()
};
let Ok(layout) = Layout::from_size_align(size as usize, align) else {
return Ok(0);
2020-08-17 09:49:21 -07:00
};
let allocator = &mut invoke_context.get_syscall_context_mut()?.allocator;
if free_addr == 0 {
2022-04-11 16:05:09 -07:00
match allocator.alloc(layout) {
2022-11-09 11:39:38 -08:00
Ok(addr) => Ok(addr),
Err(_) => Ok(0),
}
} else {
// Unimplemented
Ok(0)
}
}
);
2020-04-18 17:04:13 -07:00
fn translate_and_check_program_address_inputs<'a>(
seeds_addr: u64,
seeds_len: u64,
program_id_addr: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
check_aligned: bool,
check_size: bool,
) -> Result<(Vec<&'a [u8]>, &'a Pubkey), Error> {
let untranslated_seeds = translate_slice::<&[u8]>(
memory_mapping,
seeds_addr,
seeds_len,
check_aligned,
check_size,
)?;
if untranslated_seeds.len() > MAX_SEEDS {
return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
}
let seeds = untranslated_seeds
.iter()
.map(|untranslated_seed| {
2021-10-28 09:04:03 -07:00
if untranslated_seed.len() > MAX_SEED_LEN {
return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
}
translate_slice::<u8>(
memory_mapping,
untranslated_seed.as_ptr() as *const _ as u64,
untranslated_seed.len() as u64,
check_aligned,
check_size,
)
})
.collect::<Result<Vec<_>, Error>>()?;
let program_id = translate_type::<Pubkey>(memory_mapping, program_id_addr, check_aligned)?;
Ok((seeds, program_id))
}
declare_syscall!(
/// Create a program address
SyscallCreateProgramAddress,
fn inner_call(
invoke_context: &mut InvokeContext,
seeds_addr: u64,
seeds_len: u64,
program_id_addr: u64,
address_addr: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;
consume_compute_meter(invoke_context, cost)?;
let (seeds, program_id) = translate_and_check_program_address_inputs(
seeds_addr,
seeds_len,
program_id_addr,
memory_mapping,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let Ok(new_address) = Pubkey::create_program_address(&seeds, program_id) else {
return Ok(1);
};
let address = translate_slice_mut::<u8>(
memory_mapping,
address_addr,
32,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
address.copy_from_slice(new_address.as_ref());
Ok(0)
}
);
declare_syscall!(
/// Create a program address
SyscallTryFindProgramAddress,
fn inner_call(
invoke_context: &mut InvokeContext,
seeds_addr: u64,
seeds_len: u64,
program_id_addr: u64,
address_addr: u64,
bump_seed_addr: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;
consume_compute_meter(invoke_context, cost)?;
let (seeds, program_id) = translate_and_check_program_address_inputs(
seeds_addr,
seeds_len,
program_id_addr,
memory_mapping,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let mut bump_seed = [std::u8::MAX];
for _ in 0..std::u8::MAX {
{
let mut seeds_with_bump = seeds.to_vec();
seeds_with_bump.push(&bump_seed);
2021-10-28 09:04:03 -07:00
if let Ok(new_address) =
Pubkey::create_program_address(&seeds_with_bump, program_id)
{
let bump_seed_ref = translate_type_mut::<u8>(
memory_mapping,
bump_seed_addr,
invoke_context.get_check_aligned(),
)?;
let address = translate_slice_mut::<u8>(
memory_mapping,
address_addr,
std::mem::size_of::<Pubkey>() as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
if !is_nonoverlapping(
bump_seed_ref as *const _ as usize,
std::mem::size_of_val(bump_seed_ref),
address.as_ptr() as usize,
std::mem::size_of::<Pubkey>(),
) {
return Err(SyscallError::CopyOverlapping.into());
}
2021-10-28 09:04:03 -07:00
*bump_seed_ref = bump_seed[0];
address.copy_from_slice(new_address.as_ref());
return Ok(0);
}
}
2022-03-02 14:50:16 -08:00
bump_seed[0] = bump_seed[0].saturating_sub(1);
consume_compute_meter(invoke_context, cost)?;
}
Ok(1)
}
);
declare_syscall!(
/// SHA256
SyscallSha256,
fn inner_call(
invoke_context: &mut InvokeContext,
2020-09-29 23:29:20 -07:00
vals_addr: u64,
vals_len: u64,
result_addr: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
2021-12-15 14:19:47 -08:00
let compute_budget = invoke_context.get_compute_budget();
if compute_budget.sha256_max_slices < vals_len {
ic_msg!(
invoke_context,
"Sha256 hashing {} sequences in one syscall is over the limit {}",
vals_len,
compute_budget.sha256_max_slices,
);
return Err(SyscallError::TooManySlices.into());
}
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let mut hasher = Hasher::default();
if vals_len > 0 {
let vals = translate_slice::<&[u8]>(
memory_mapping,
vals_addr,
vals_len,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
for val in vals.iter() {
let bytes = translate_slice::<u8>(
memory_mapping,
val.as_ptr() as u64,
val.len() as u64,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let cost = compute_budget.mem_op_base_cost.max(
compute_budget.sha256_byte_cost.saturating_mul(
(val.len() as u64)
.checked_div(2)
.expect("div by non-zero literal"),
),
);
consume_compute_meter(invoke_context, cost)?;
2020-09-29 23:29:20 -07:00
hasher.hash(bytes);
}
}
hash_result.copy_from_slice(&hasher.result().to_bytes());
Ok(0)
2020-09-29 23:29:20 -07:00
}
);
2020-09-29 23:29:20 -07:00
declare_syscall!(
// Keccak256
SyscallKeccak256,
fn inner_call(
invoke_context: &mut InvokeContext,
vals_addr: u64,
vals_len: u64,
result_addr: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
2021-12-15 14:19:47 -08:00
let compute_budget = invoke_context.get_compute_budget();
if compute_budget.sha256_max_slices < vals_len {
ic_msg!(
invoke_context,
"Keccak256 hashing {} sequences in one syscall is over the limit {}",
vals_len,
compute_budget.sha256_max_slices,
);
return Err(SyscallError::TooManySlices.into());
}
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
keccak::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let mut hasher = keccak::Hasher::default();
if vals_len > 0 {
let vals = translate_slice::<&[u8]>(
memory_mapping,
vals_addr,
vals_len,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
for val in vals.iter() {
let bytes = translate_slice::<u8>(
memory_mapping,
val.as_ptr() as u64,
val.len() as u64,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let cost = compute_budget.mem_op_base_cost.max(
compute_budget.sha256_byte_cost.saturating_mul(
(val.len() as u64)
.checked_div(2)
.expect("div by non-zero literal"),
),
);
consume_compute_meter(invoke_context, cost)?;
hasher.hash(bytes);
}
}
hash_result.copy_from_slice(&hasher.result().to_bytes());
Ok(0)
}
);
declare_syscall!(
/// secp256k1_recover
SyscallSecp256k1Recover,
fn inner_call(
invoke_context: &mut InvokeContext,
hash_addr: u64,
recovery_id_val: u64,
signature_addr: u64,
result_addr: u64,
2021-06-01 15:33:17 -07:00
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let cost = invoke_context.get_compute_budget().secp256k1_recover_cost;
consume_compute_meter(invoke_context, cost)?;
2021-06-01 15:33:17 -07:00
let hash = translate_slice::<u8>(
memory_mapping,
hash_addr,
keccak::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let signature = translate_slice::<u8>(
memory_mapping,
signature_addr,
SECP256K1_SIGNATURE_LENGTH as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let secp256k1_recover_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
SECP256K1_PUBLIC_KEY_LENGTH as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let Ok(message) = libsecp256k1::Message::parse_slice(hash) else {
return Ok(Secp256k1RecoverError::InvalidHash.into());
};
let Ok(adjusted_recover_id_val) = recovery_id_val.try_into() else {
return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
2022-03-14 09:34:43 -07:00
};
let Ok(recovery_id) = libsecp256k1::RecoveryId::parse(adjusted_recover_id_val) else {
return Ok(Secp256k1RecoverError::InvalidRecoveryId.into());
};
let sig_parse_result = if invoke_context
.feature_set
.is_active(&libsecp256k1_0_5_upgrade_enabled::id())
{
libsecp256k1::Signature::parse_standard_slice(signature)
} else {
libsecp256k1::Signature::parse_overflowing_slice(signature)
};
2021-07-13 21:22:35 -07:00
let Ok(signature) = sig_parse_result else {
return Ok(Secp256k1RecoverError::InvalidSignature.into());
};
let public_key = match libsecp256k1::recover(&message, &signature, &recovery_id) {
Ok(key) => key.serialize(),
Err(_) => {
return Ok(Secp256k1RecoverError::InvalidSignature.into());
}
};
secp256k1_recover_result.copy_from_slice(&public_key[1..65]);
Ok(SUCCESS)
}
);
declare_syscall!(
// Elliptic Curve Point Validation
//
// Currently, only curve25519 Edwards and Ristretto representations are supported
SyscallCurvePointValidation,
fn inner_call(
invoke_context: &mut InvokeContext,
curve_id: u64,
point_addr: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
use solana_zk_token_sdk::curve25519::{curve_syscall_traits::*, edwards, ristretto};
match curve_id {
CURVE25519_EDWARDS => {
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_validate_point_cost;
consume_compute_meter(invoke_context, cost)?;
let point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
point_addr,
invoke_context.get_check_aligned(),
)?;
if edwards::validate_edwards(point) {
Ok(0)
} else {
Ok(1)
}
}
CURVE25519_RISTRETTO => {
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_validate_point_cost;
consume_compute_meter(invoke_context, cost)?;
let point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
point_addr,
invoke_context.get_check_aligned(),
)?;
if ristretto::validate_ristretto(point) {
Ok(0)
} else {
Ok(1)
}
}
_ => Ok(1),
}
}
);
declare_syscall!(
// Elliptic Curve Group Operations
//
// Currently, only curve25519 Edwards and Ristretto representations are supported
SyscallCurveGroupOps,
fn inner_call(
invoke_context: &mut InvokeContext,
curve_id: u64,
group_op: u64,
left_input_addr: u64,
right_input_addr: u64,
result_point_addr: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
use solana_zk_token_sdk::curve25519::{
curve_syscall_traits::*, edwards, ristretto, scalar,
};
match curve_id {
CURVE25519_EDWARDS => match group_op {
ADD => {
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_add_cost;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let right_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) = edwards::add_edwards(left_point, right_point) {
*translate_type_mut::<edwards::PodEdwardsPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
SUB => {
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_subtract_cost;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let right_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) = edwards::subtract_edwards(left_point, right_point) {
*translate_type_mut::<edwards::PodEdwardsPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
MUL => {
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_multiply_cost;
consume_compute_meter(invoke_context, cost)?;
let scalar = translate_type::<scalar::PodScalar>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let input_point = translate_type::<edwards::PodEdwardsPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) = edwards::multiply_edwards(scalar, input_point) {
*translate_type_mut::<edwards::PodEdwardsPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
_ => Ok(1),
},
CURVE25519_RISTRETTO => match group_op {
ADD => {
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_add_cost;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let right_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) = ristretto::add_ristretto(left_point, right_point) {
*translate_type_mut::<ristretto::PodRistrettoPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
SUB => {
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_subtract_cost;
consume_compute_meter(invoke_context, cost)?;
let left_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let right_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) =
ristretto::subtract_ristretto(left_point, right_point)
{
*translate_type_mut::<ristretto::PodRistrettoPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
MUL => {
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_multiply_cost;
consume_compute_meter(invoke_context, cost)?;
let scalar = translate_type::<scalar::PodScalar>(
memory_mapping,
left_input_addr,
invoke_context.get_check_aligned(),
)?;
let input_point = translate_type::<ristretto::PodRistrettoPoint>(
memory_mapping,
right_input_addr,
invoke_context.get_check_aligned(),
)?;
if let Some(result_point) = ristretto::multiply_ristretto(scalar, input_point) {
*translate_type_mut::<ristretto::PodRistrettoPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
_ => Ok(1),
},
_ => Ok(1),
}
}
);
declare_syscall!(
// Elliptic Curve Multiscalar Multiplication
//
// Currently, only curve25519 Edwards and Ristretto representations are supported
SyscallCurveMultiscalarMultiplication,
fn inner_call(
invoke_context: &mut InvokeContext,
curve_id: u64,
scalars_addr: u64,
points_addr: u64,
points_len: u64,
result_point_addr: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
use solana_zk_token_sdk::curve25519::{
curve_syscall_traits::*, edwards, ristretto, scalar,
};
match curve_id {
CURVE25519_EDWARDS => {
let cost = invoke_context
.get_compute_budget()
.curve25519_edwards_msm_base_cost
.saturating_add(
invoke_context
.get_compute_budget()
.curve25519_edwards_msm_incremental_cost
.saturating_mul(points_len.saturating_sub(1)),
);
consume_compute_meter(invoke_context, cost)?;
let scalars = translate_slice::<scalar::PodScalar>(
memory_mapping,
scalars_addr,
points_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let points = translate_slice::<edwards::PodEdwardsPoint>(
memory_mapping,
points_addr,
points_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
if let Some(result_point) = edwards::multiscalar_multiply_edwards(scalars, points) {
*translate_type_mut::<edwards::PodEdwardsPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
CURVE25519_RISTRETTO => {
let cost = invoke_context
.get_compute_budget()
.curve25519_ristretto_msm_base_cost
.saturating_add(
invoke_context
.get_compute_budget()
.curve25519_ristretto_msm_incremental_cost
.saturating_mul(points_len.saturating_sub(1)),
);
consume_compute_meter(invoke_context, cost)?;
let scalars = translate_slice::<scalar::PodScalar>(
memory_mapping,
scalars_addr,
points_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let points = translate_slice::<ristretto::PodRistrettoPoint>(
memory_mapping,
points_addr,
points_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
if let Some(result_point) =
ristretto::multiscalar_multiply_ristretto(scalars, points)
{
*translate_type_mut::<ristretto::PodRistrettoPoint>(
memory_mapping,
result_point_addr,
invoke_context.get_check_aligned(),
)? = result_point;
Ok(0)
} else {
Ok(1)
}
}
_ => Ok(1),
}
}
);
declare_syscall!(
// Blake3
SyscallBlake3,
fn inner_call(
invoke_context: &mut InvokeContext,
2021-06-08 11:04:10 -07:00
vals_addr: u64,
vals_len: u64,
result_addr: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
2021-12-15 14:19:47 -08:00
let compute_budget = invoke_context.get_compute_budget();
if compute_budget.sha256_max_slices < vals_len {
ic_msg!(
invoke_context,
"Blake3 hashing {} sequences in one syscall is over the limit {}",
vals_len,
compute_budget.sha256_max_slices,
);
return Err(SyscallError::TooManySlices.into());
}
consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
blake3::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let mut hasher = blake3::Hasher::default();
if vals_len > 0 {
let vals = translate_slice::<&[u8]>(
2021-06-08 11:04:10 -07:00
memory_mapping,
vals_addr,
vals_len,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
for val in vals.iter() {
let bytes = translate_slice::<u8>(
memory_mapping,
val.as_ptr() as u64,
val.len() as u64,
2022-04-11 16:05:09 -07:00
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let cost = compute_budget.mem_op_base_cost.max(
compute_budget.sha256_byte_cost.saturating_mul(
(val.len() as u64)
.checked_div(2)
.expect("div by non-zero literal"),
),
);
consume_compute_meter(invoke_context, cost)?;
2021-06-08 11:04:10 -07:00
hasher.hash(bytes);
}
}
hash_result.copy_from_slice(&hasher.result().to_bytes());
Ok(0)
2021-06-08 11:04:10 -07:00
}
);
2021-06-08 11:04:10 -07:00
declare_syscall!(
/// Set return data
SyscallSetReturnData,
fn inner_call(
invoke_context: &mut InvokeContext,
addr: u64,
len: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let budget = invoke_context.get_compute_budget();
let cost = len
.checked_div(budget.cpi_bytes_per_unit)
.unwrap_or(u64::MAX)
.saturating_add(budget.syscall_base_cost);
consume_compute_meter(invoke_context, cost)?;
if len > MAX_RETURN_DATA as u64 {
return Err(SyscallError::ReturnDataTooLarge(len, MAX_RETURN_DATA as u64).into());
}
let return_data = if len == 0 {
Vec::new()
} else {
translate_slice::<u8>(
memory_mapping,
addr,
len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?
.to_vec()
};
let transaction_context = &mut invoke_context.transaction_context;
let program_id = *transaction_context
.get_current_instruction_context()
.and_then(|instruction_context| {
instruction_context.get_last_program_key(transaction_context)
})?;
transaction_context.set_return_data(program_id, return_data)?;
Ok(0)
}
);
declare_syscall!(
/// Get return data
SyscallGetReturnData,
fn inner_call(
invoke_context: &mut InvokeContext,
return_data_addr: u64,
mut length: u64,
program_id_addr: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let budget = invoke_context.get_compute_budget();
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
let (program_id, return_data) = invoke_context.transaction_context.get_return_data();
length = length.min(return_data.len() as u64);
if length != 0 {
let cost = length
.saturating_add(size_of::<Pubkey>() as u64)
.checked_div(budget.cpi_bytes_per_unit)
.unwrap_or(u64::MAX);
consume_compute_meter(invoke_context, cost)?;
let return_data_result = translate_slice_mut::<u8>(
memory_mapping,
return_data_addr,
length,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
2022-03-10 11:48:33 -08:00
let to_slice = return_data_result;
let from_slice = return_data
.get(..length as usize)
.ok_or(SyscallError::InvokeContextBorrowFailed)?;
2022-03-10 11:48:33 -08:00
if to_slice.len() != from_slice.len() {
return Err(SyscallError::InvalidLength.into());
2022-03-10 11:48:33 -08:00
}
to_slice.copy_from_slice(from_slice);
let program_id_result = translate_type_mut::<Pubkey>(
memory_mapping,
program_id_addr,
invoke_context.get_check_aligned(),
)?;
if !is_nonoverlapping(
to_slice.as_ptr() as usize,
length as usize,
program_id_result as *const _ as usize,
std::mem::size_of::<Pubkey>(),
) {
return Err(SyscallError::CopyOverlapping.into());
}
2022-03-10 11:48:33 -08:00
*program_id_result = *program_id;
}
// Return the actual length, rather the length returned
Ok(return_data.len() as u64)
}
);
declare_syscall!(
/// Get a processed sigling instruction
SyscallGetProcessedSiblingInstruction,
fn inner_call(
invoke_context: &mut InvokeContext,
index: u64,
meta_addr: u64,
program_id_addr: u64,
data_addr: u64,
accounts_addr: u64,
2022-05-19 00:19:05 -07:00
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let budget = invoke_context.get_compute_budget();
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
let stop_sibling_instruction_search_at_parent = invoke_context
.feature_set
.is_active(&stop_sibling_instruction_search_at_parent::id());
// Reverse iterate through the instruction trace,
// ignoring anything except instructions on the same level
let stack_height = invoke_context.get_stack_height();
let instruction_trace_length = invoke_context
.transaction_context
.get_instruction_trace_length();
let mut reverse_index_at_stack_height = 0;
let mut found_instruction_context = None;
for index_in_trace in (0..instruction_trace_length).rev() {
let instruction_context = invoke_context
.transaction_context
.get_instruction_context_at_index_in_trace(index_in_trace)?;
if (stop_sibling_instruction_search_at_parent
|| instruction_context.get_stack_height() == TRANSACTION_LEVEL_STACK_HEIGHT)
&& instruction_context.get_stack_height() < stack_height
{
break;
}
if instruction_context.get_stack_height() == stack_height {
if index.saturating_add(1) == reverse_index_at_stack_height {
found_instruction_context = Some(instruction_context);
break;
}
reverse_index_at_stack_height = reverse_index_at_stack_height.saturating_add(1);
}
}
if let Some(instruction_context) = found_instruction_context {
let result_header = translate_type_mut::<ProcessedSiblingInstruction>(
memory_mapping,
meta_addr,
invoke_context.get_check_aligned(),
)?;
if result_header.data_len == (instruction_context.get_instruction_data().len() as u64)
&& result_header.accounts_len
== (instruction_context.get_number_of_instruction_accounts() as u64)
{
let program_id = translate_type_mut::<Pubkey>(
memory_mapping,
program_id_addr,
invoke_context.get_check_aligned(),
)?;
let data = translate_slice_mut::<u8>(
memory_mapping,
data_addr,
2022-11-09 11:39:38 -08:00
result_header.data_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let accounts = translate_slice_mut::<AccountMeta>(
memory_mapping,
accounts_addr,
2022-11-09 11:39:38 -08:00
result_header.accounts_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
if !is_nonoverlapping(
result_header as *const _ as usize,
std::mem::size_of::<ProcessedSiblingInstruction>(),
program_id as *const _ as usize,
std::mem::size_of::<Pubkey>(),
) || !is_nonoverlapping(
result_header as *const _ as usize,
std::mem::size_of::<ProcessedSiblingInstruction>(),
accounts.as_ptr() as usize,
std::mem::size_of::<AccountMeta>()
.saturating_mul(result_header.accounts_len as usize),
) || !is_nonoverlapping(
result_header as *const _ as usize,
std::mem::size_of::<ProcessedSiblingInstruction>(),
data.as_ptr() as usize,
result_header.data_len as usize,
) || !is_nonoverlapping(
program_id as *const _ as usize,
std::mem::size_of::<Pubkey>(),
data.as_ptr() as usize,
result_header.data_len as usize,
) || !is_nonoverlapping(
program_id as *const _ as usize,
std::mem::size_of::<Pubkey>(),
accounts.as_ptr() as usize,
std::mem::size_of::<AccountMeta>()
.saturating_mul(result_header.accounts_len as usize),
) || !is_nonoverlapping(
data.as_ptr() as usize,
result_header.data_len as usize,
accounts.as_ptr() as usize,
std::mem::size_of::<AccountMeta>()
.saturating_mul(result_header.accounts_len as usize),
) {
return Err(SyscallError::CopyOverlapping.into());
}
*program_id = *instruction_context
.get_last_program_key(invoke_context.transaction_context)?;
data.clone_from_slice(instruction_context.get_instruction_data());
let account_metas = (0..instruction_context.get_number_of_instruction_accounts())
.map(|instruction_account_index| {
Ok(AccountMeta {
pubkey: *invoke_context
.transaction_context
.get_key_of_account_at_index(
instruction_context
.get_index_of_instruction_account_in_transaction(
instruction_account_index,
)?,
)?,
is_signer: instruction_context
.is_instruction_account_signer(instruction_account_index)?,
is_writable: instruction_context
.is_instruction_account_writable(instruction_account_index)?,
})
})
.collect::<Result<Vec<_>, InstructionError>>()?;
accounts.clone_from_slice(account_metas.as_slice());
}
result_header.data_len = instruction_context.get_instruction_data().len() as u64;
result_header.accounts_len =
instruction_context.get_number_of_instruction_accounts() as u64;
return Ok(true as u64);
}
Ok(false as u64)
}
);
declare_syscall!(
/// Get current call stack height
SyscallGetStackHeight,
fn inner_call(
invoke_context: &mut InvokeContext,
_arg1: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
2022-05-19 00:19:05 -07:00
_memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let budget = invoke_context.get_compute_budget();
consume_compute_meter(invoke_context, budget.syscall_base_cost)?;
Ok(invoke_context.get_stack_height() as u64)
}
);
declare_syscall!(
/// alt_bn128 group operations
SyscallAltBn128,
fn inner_call(
invoke_context: &mut InvokeContext,
group_op: u64,
input_addr: u64,
input_size: u64,
result_addr: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
use solana_sdk::alt_bn128::prelude::{ALT_BN128_ADD, ALT_BN128_MUL, ALT_BN128_PAIRING};
let budget = invoke_context.get_compute_budget();
let (cost, output): (u64, usize) = match group_op {
ALT_BN128_ADD => (
budget.alt_bn128_addition_cost,
ALT_BN128_ADDITION_OUTPUT_LEN,
),
ALT_BN128_MUL => (
budget.alt_bn128_multiplication_cost,
ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
),
ALT_BN128_PAIRING => {
let ele_len = input_size
.checked_div(ALT_BN128_PAIRING_ELEMENT_LEN as u64)
.expect("div by non-zero constant");
let cost = budget
.alt_bn128_pairing_one_pair_cost_first
.saturating_add(
budget
.alt_bn128_pairing_one_pair_cost_other
.saturating_mul(ele_len.saturating_sub(1)),
)
.saturating_add(budget.sha256_base_cost)
.saturating_add(input_size)
.saturating_add(ALT_BN128_PAIRING_OUTPUT_LEN as u64);
(cost, ALT_BN128_PAIRING_OUTPUT_LEN)
}
_ => {
return Err(SyscallError::InvalidAttribute.into());
}
};
consume_compute_meter(invoke_context, cost)?;
let input = translate_slice::<u8>(
memory_mapping,
input_addr,
input_size,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let call_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
output as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let calculation = match group_op {
ALT_BN128_ADD => alt_bn128_addition,
ALT_BN128_MUL => alt_bn128_multiplication,
ALT_BN128_PAIRING => alt_bn128_pairing,
_ => {
return Err(SyscallError::InvalidAttribute.into());
}
};
let result_point = match calculation(input) {
Ok(result_point) => result_point,
Err(e) => {
return Ok(e.into());
}
};
if result_point.len() != output {
return Ok(AltBn128Error::SliceOutOfBounds.into());
}
call_result.copy_from_slice(&result_point);
Ok(SUCCESS)
}
);
declare_syscall!(
/// Big integer modular exponentiation
SyscallBigModExp,
fn inner_call(
invoke_context: &mut InvokeContext,
params: u64,
return_value: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let params = &translate_slice::<BigModExpParams>(
memory_mapping,
params,
1,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?
.get(0)
.ok_or(SyscallError::InvalidLength)?;
if params.base_len > 512 || params.exponent_len > 512 || params.modulus_len > 512 {
return Err(Box::new(SyscallError::InvalidLength));
}
let input_len: u64 = std::cmp::max(params.base_len, params.exponent_len);
let input_len: u64 = std::cmp::max(input_len, params.modulus_len);
let budget = invoke_context.get_compute_budget();
consume_compute_meter(
invoke_context,
budget.syscall_base_cost.saturating_add(
input_len
.saturating_mul(input_len)
.checked_div(budget.big_modular_exponentiation_cost)
.unwrap_or(u64::MAX),
),
)?;
let base = translate_slice::<u8>(
memory_mapping,
params.base as *const _ as u64,
params.base_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let exponent = translate_slice::<u8>(
memory_mapping,
params.exponent as *const _ as u64,
params.exponent_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let modulus = translate_slice::<u8>(
memory_mapping,
params.modulus as *const _ as u64,
params.modulus_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let value = big_mod_exp(base, exponent, modulus);
let return_value = translate_slice_mut::<u8>(
memory_mapping,
return_value,
params.modulus_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
return_value.copy_from_slice(value.as_slice());
Ok(0)
}
);
declare_syscall!(
// Poseidon
SyscallPoseidon,
fn inner_call(
invoke_context: &mut InvokeContext,
parameters: u64,
endianness: u64,
vals_addr: u64,
vals_len: u64,
result_addr: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let parameters: poseidon::Parameters = parameters.try_into()?;
let endianness: poseidon::Endianness = endianness.try_into()?;
if vals_len > 12 {
ic_msg!(
invoke_context,
"Poseidon hashing {} sequences is not supported",
vals_len,
);
return Err(SyscallError::InvalidLength.into());
}
let budget = invoke_context.get_compute_budget();
let Some(cost) = budget.poseidon_cost(vals_len) else {
ic_msg!(
invoke_context,
"Overflow while calculating the compute cost"
);
return Err(SyscallError::ArithmeticOverflow.into());
};
consume_compute_meter(invoke_context, cost.to_owned())?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
poseidon::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let inputs = translate_slice::<&[u8]>(
memory_mapping,
vals_addr,
vals_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let inputs = inputs
.iter()
.map(|input| {
translate_slice::<u8>(
memory_mapping,
input.as_ptr() as *const _ as u64,
input.len() as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)
})
.collect::<Result<Vec<_>, Error>>()?;
let hash = match poseidon::hashv(parameters, endianness, inputs.as_slice()) {
Ok(hash) => hash,
Err(e) => {
return Ok(e.into());
}
};
hash_result.copy_from_slice(&hash.to_bytes());
Ok(SUCCESS)
}
);
2020-04-18 17:04:13 -07:00
#[cfg(test)]
#[allow(clippy::arithmetic_side_effects)]
#[allow(clippy::indexing_slicing)]
2020-04-18 17:04:13 -07:00
mod tests {
#[allow(deprecated)]
use solana_sdk::sysvar::fees::Fees;
use {
super::*,
crate::mock_create_vm,
assert_matches::assert_matches,
core::slice,
solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
solana_rbpf::{
2023-07-05 10:46:21 -07:00
elf::SBPFVersion,
error::EbpfError,
memory_region::MemoryRegion,
vm::{BuiltinFunction, Config},
},
solana_sdk::{
account::{create_account_shared_data_for_test, AccountSharedData},
bpf_loader,
fee_calculator::FeeCalculator,
hash::hashv,
instruction::Instruction,
2022-03-30 08:28:49 -07:00
program::check_type_assumptions,
stable_layout::stable_instruction::StableInstruction,
sysvar::{
self, clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule,
},
},
std::{mem, str::FromStr},
};
2020-04-18 17:04:13 -07:00
macro_rules! assert_access_violation {
($result:expr, $va:expr, $len:expr) => {
match $result.unwrap_err().downcast_ref::<EbpfError>().unwrap() {
EbpfError::AccessViolation(_, _, va, len, _) if $va == *va && $len == *len => {}
EbpfError::StackAccessViolation(_, _, va, len, _) if $va == *va && $len == *len => {
}
_ => panic!(),
}
};
}
macro_rules! prepare_mockup {
($invoke_context:ident,
$program_key:ident,
$loader_key:expr $(,)?) => {
let $program_key = Pubkey::new_unique();
let transaction_accounts = vec![
(
$loader_key,
AccountSharedData::new(0, 0, &native_loader::id()),
),
($program_key, AccountSharedData::new(0, 0, &$loader_key)),
];
with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
$invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(&[0, 1], &[], &[]);
$invoke_context.push().unwrap();
};
}
#[allow(dead_code)]
struct MockSlice {
pub vm_addr: u64,
pub len: usize,
}
2020-04-18 17:04:13 -07:00
#[test]
fn test_translate() {
const START: u64 = 0x100000000;
2020-04-18 17:04:13 -07:00
const LENGTH: u64 = 1000;
2020-04-18 17:04:13 -07:00
let data = vec![0u8; LENGTH as usize];
let addr = data.as_ptr() as u64;
let config = Config::default();
2023-07-05 10:46:21 -07:00
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(&data, START)],
&config,
&SBPFVersion::V2,
)
.unwrap();
2020-04-18 17:04:13 -07:00
let cases = vec![
(true, START, 0, addr),
(true, START, 1, addr),
(true, START, LENGTH, addr),
(true, START + 1, LENGTH - 1, addr + 1),
(false, START + 1, LENGTH, 0),
(true, START + LENGTH - 1, 1, addr + LENGTH - 1),
(true, START + LENGTH, 0, addr + LENGTH),
(false, START + LENGTH, 1, 0),
(false, START, LENGTH + 1, 0),
(false, 0, 0, 0),
(false, 0, 1, 0),
(false, START - 1, 0, 0),
(false, START - 1, 1, 0),
(true, START + LENGTH / 2, LENGTH / 2, addr + LENGTH / 2),
];
for (ok, start, length, value) in cases {
if ok {
assert_eq!(
2021-03-10 09:48:41 -08:00
translate(&memory_mapping, AccessType::Load, start, length).unwrap(),
value
)
} else {
2021-03-10 09:48:41 -08:00
assert!(translate(&memory_mapping, AccessType::Load, start, length).is_err())
2020-04-18 17:04:13 -07:00
}
}
}
#[test]
fn test_translate_type() {
let config = Config::default();
2020-04-18 17:04:13 -07:00
// Pubkey
let pubkey = solana_sdk::pubkey::new_rand();
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let translated_pubkey =
translate_type::<Pubkey>(&memory_mapping, 0x100000000, true).unwrap();
2020-04-18 17:04:13 -07:00
assert_eq!(pubkey, *translated_pubkey);
// Instruction
2021-03-03 21:46:48 -08:00
let instruction = Instruction::new_with_bincode(
solana_sdk::pubkey::new_rand(),
2020-04-18 17:04:13 -07:00
&"foobar",
vec![AccountMeta::new(solana_sdk::pubkey::new_rand(), false)],
2020-04-18 17:04:13 -07:00
);
let instruction = StableInstruction::from(instruction);
let memory_region = MemoryRegion::new_readonly(bytes_of(&instruction), 0x100000000);
2023-07-05 10:46:21 -07:00
let memory_mapping =
MemoryMapping::new(vec![memory_region], &config, &SBPFVersion::V2).unwrap();
let translated_instruction =
translate_type::<StableInstruction>(&memory_mapping, 0x100000000, true).unwrap();
2020-04-18 17:04:13 -07:00
assert_eq!(instruction, *translated_instruction);
let memory_region = MemoryRegion::new_readonly(&bytes_of(&instruction)[..1], 0x100000000);
2023-07-05 10:46:21 -07:00
let memory_mapping =
MemoryMapping::new(vec![memory_region], &config, &SBPFVersion::V2).unwrap();
assert!(translate_type::<Instruction>(&memory_mapping, 0x100000000, true).is_err());
2020-04-18 17:04:13 -07:00
}
#[test]
fn test_translate_slice() {
let config = Config::default();
// zero len
let good_data = vec![1u8, 2, 3, 4, 5];
let data: Vec<u8> = vec![];
assert_eq!(0x1 as *const u8, data.as_ptr());
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(&good_data, 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-09-17 09:46:49 -07:00
let translated_data =
translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, 0, true, true).unwrap();
assert_eq!(data, translated_data);
assert_eq!(0, translated_data.len());
2020-04-18 17:04:13 -07:00
// u8
let mut data = vec![1u8, 2, 3, 4, 5];
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(&data, 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let translated_data =
translate_slice::<u8>(&memory_mapping, 0x100000000, data.len() as u64, true, true)
.unwrap();
2020-04-18 17:04:13 -07:00
assert_eq!(data, translated_data);
2022-03-10 11:48:33 -08:00
*data.first_mut().unwrap() = 10;
2020-04-18 17:04:13 -07:00
assert_eq!(data, translated_data);
assert!(
translate_slice::<u8>(&memory_mapping, data.as_ptr() as u64, u64::MAX, true, true)
.is_err()
);
assert!(translate_slice::<u8>(
&memory_mapping,
0x100000000 - 1,
data.len() as u64,
true,
true
)
.is_err());
2020-04-18 17:04:13 -07:00
// u64
let mut data = vec![1u64, 2, 3, 4, 5];
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(
bytes_of_slice(&data),
0x100000000,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let translated_data =
translate_slice::<u64>(&memory_mapping, 0x100000000, data.len() as u64, true, true)
.unwrap();
assert_eq!(data, translated_data);
2022-03-10 11:48:33 -08:00
*data.first_mut().unwrap() = 10;
assert_eq!(data, translated_data);
2021-09-17 09:46:49 -07:00
assert!(
translate_slice::<u64>(&memory_mapping, 0x100000000, u64::MAX, true, true).is_err()
2021-09-17 09:46:49 -07:00
);
2020-04-18 17:04:13 -07:00
// Pubkeys
let mut data = vec![solana_sdk::pubkey::new_rand(); 5];
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(
unsafe {
slice::from_raw_parts(data.as_ptr() as *const u8, mem::size_of::<Pubkey>() * 5)
},
0x100000000,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let translated_data =
translate_slice::<Pubkey>(&memory_mapping, 0x100000000, data.len() as u64, true, true)
.unwrap();
2020-04-18 17:04:13 -07:00
assert_eq!(data, translated_data);
2022-03-10 11:48:33 -08:00
*data.first_mut().unwrap() = solana_sdk::pubkey::new_rand(); // Both should point to same place
2020-04-18 17:04:13 -07:00
assert_eq!(data, translated_data);
}
#[test]
fn test_translate_string_and_do() {
let string = "Gaggablaghblagh!";
let config = Config::default();
let memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2020-04-18 17:04:13 -07:00
assert_eq!(
42,
translate_string_and_do(
&memory_mapping,
0x100000000,
string.len() as u64,
true,
true,
true,
&mut |string: &str| {
assert_eq!(string, "Gaggablaghblagh!");
Ok(42)
}
)
2020-04-18 17:04:13 -07:00
.unwrap()
);
}
#[test]
#[should_panic(expected = "Abort")]
fn test_syscall_abort() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let config = Config::default();
2023-07-05 10:46:21 -07:00
let mut memory_mapping = MemoryMapping::new(vec![], &config, &SBPFVersion::V2).unwrap();
let mut result = ProgramResult::Ok(0);
SyscallAbort::call(
&mut invoke_context,
0,
0,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
);
result.unwrap();
2020-04-18 17:04:13 -07:00
}
#[test]
#[should_panic(expected = "Panic(\"Gaggablaghblagh!\", 42, 84)")]
fn test_syscall_sol_panic() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2020-04-18 17:04:13 -07:00
let string = "Gaggablaghblagh!";
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-02-11 16:55:17 -08:00
invoke_context.mock_set_remaining(string.len() as u64 - 1);
let mut result = ProgramResult::Ok(0);
SyscallPanic::call(
&mut invoke_context,
0x100000000,
2021-02-11 16:55:17 -08:00
string.len() as u64,
42,
84,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
2021-02-11 16:55:17 -08:00
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
2021-02-11 16:55:17 -08:00
invoke_context.mock_set_remaining(string.len() as u64);
let mut result = ProgramResult::Ok(0);
SyscallPanic::call(
&mut invoke_context,
0x100000000,
string.len() as u64,
42,
84,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
);
result.unwrap();
2020-04-18 17:04:13 -07:00
}
#[test]
fn test_syscall_sol_log() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
2020-04-18 17:04:13 -07:00
let string = "Gaggablaghblagh!";
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(string.as_bytes(), 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(400 - 1);
let mut result = ProgramResult::Ok(0);
SyscallLog::call(
&mut invoke_context,
0x100000001, // AccessViolation
string.len() as u64,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
);
assert_access_violation!(result, 0x100000001, string.len() as u64);
let mut result = ProgramResult::Ok(0);
SyscallLog::call(
&mut invoke_context,
0x100000000,
string.len() as u64 * 2, // AccessViolation
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
);
assert_access_violation!(result, 0x100000000, string.len() as u64 * 2);
2021-02-11 16:55:17 -08:00
let mut result = ProgramResult::Ok(0);
SyscallLog::call(
&mut invoke_context,
0x100000000,
2021-02-11 16:55:17 -08:00
string.len() as u64,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
2021-02-11 16:55:17 -08:00
&mut result,
);
result.unwrap();
let mut result = ProgramResult::Ok(0);
SyscallLog::call(
&mut invoke_context,
0x100000000,
2021-02-11 16:55:17 -08:00
string.len() as u64,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
2021-02-11 16:55:17 -08:00
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
assert_eq!(
invoke_context
.get_log_collector()
.unwrap()
.borrow()
.get_recorded_content(),
&["Program log: Gaggablaghblagh!".to_string()]
);
2020-04-18 17:04:13 -07:00
}
#[test]
fn test_syscall_sol_log_u64() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context.get_compute_budget().log_64_units;
2020-04-18 17:04:13 -07:00
invoke_context.mock_set_remaining(cost);
let config = Config::default();
2023-07-05 10:46:21 -07:00
let mut memory_mapping = MemoryMapping::new(vec![], &config, &SBPFVersion::V2).unwrap();
let mut result = ProgramResult::Ok(0);
SyscallLogU64::call(
&mut invoke_context,
1,
2,
3,
4,
5,
&mut memory_mapping,
&mut result,
);
result.unwrap();
assert_eq!(
invoke_context
.get_log_collector()
.unwrap()
.borrow()
.get_recorded_content(),
&["Program log: 0x1, 0x2, 0x3, 0x4, 0x5".to_string()]
);
2020-04-18 17:04:13 -07:00
}
2020-10-15 09:11:54 -07:00
#[test]
fn test_syscall_sol_pubkey() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context.get_compute_budget().log_pubkey_units;
2020-10-15 09:11:54 -07:00
let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap();
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_readonly(bytes_of(&pubkey), 0x100000000)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let mut result = ProgramResult::Ok(0);
SyscallLogPubkey::call(
&mut invoke_context,
0x100000001, // AccessViolation
32,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
2020-10-15 09:11:54 -07:00
);
assert_access_violation!(result, 0x100000001, 32);
invoke_context.mock_set_remaining(1);
let mut result = ProgramResult::Ok(0);
SyscallLogPubkey::call(
&mut invoke_context,
100,
32,
0,
0,
0,
&mut memory_mapping,
&mut result,
2020-10-15 09:11:54 -07:00
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
invoke_context.mock_set_remaining(cost);
let mut result = ProgramResult::Ok(0);
SyscallLogPubkey::call(
&mut invoke_context,
0x100000000,
0,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
result.unwrap();
assert_eq!(
invoke_context
.get_log_collector()
.unwrap()
.borrow()
.get_recorded_content(),
&["Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN".to_string()]
);
2020-10-15 09:11:54 -07:00
}
2020-04-18 17:04:13 -07:00
#[test]
fn test_syscall_sol_alloc_free() {
2020-04-18 17:04:13 -07:00
// large alloc
{
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
let mut vm = vm.unwrap();
2023-07-05 10:46:21 -07:00
let invoke_context = &mut vm.context_object_pointer;
let memory_mapping = &mut vm.memory_mapping;
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
solana_sdk::entrypoint::HEAP_LENGTH as u64,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
assert_ne!(result.unwrap(), 0);
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
solana_sdk::entrypoint::HEAP_LENGTH as u64,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
u64::MAX,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
2020-04-18 17:04:13 -07:00
}
2022-04-11 16:05:09 -07:00
2020-08-24 13:21:34 -07:00
// many small unaligned allocs
{
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
invoke_context.feature_set = Arc::new(FeatureSet::default());
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
let mut vm = vm.unwrap();
2023-07-05 10:46:21 -07:00
let invoke_context = &mut vm.context_object_pointer;
let memory_mapping = &mut vm.memory_mapping;
2020-08-24 13:21:34 -07:00
for _ in 0..100 {
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(invoke_context, 1, 0, 0, 0, 0, memory_mapping, &mut result);
assert_ne!(result.unwrap(), 0);
2020-08-24 13:21:34 -07:00
}
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
solana_sdk::entrypoint::HEAP_LENGTH as u64,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
2020-08-24 13:21:34 -07:00
}
2022-04-11 16:05:09 -07:00
2020-08-24 13:21:34 -07:00
// many small aligned allocs
2020-04-18 17:04:13 -07:00
{
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
let mut vm = vm.unwrap();
2023-07-05 10:46:21 -07:00
let invoke_context = &mut vm.context_object_pointer;
let memory_mapping = &mut vm.memory_mapping;
2020-08-17 09:49:21 -07:00
for _ in 0..12 {
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(invoke_context, 1, 0, 0, 0, 0, memory_mapping, &mut result);
assert_ne!(result.unwrap(), 0);
2020-04-18 17:04:13 -07:00
}
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
solana_sdk::entrypoint::HEAP_LENGTH as u64,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
2020-04-18 17:04:13 -07:00
}
2022-04-11 16:05:09 -07:00
2020-08-17 09:49:21 -07:00
// aligned allocs
fn aligned<T>() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
let mut vm = vm.unwrap();
2023-07-05 10:46:21 -07:00
let invoke_context = &mut vm.context_object_pointer;
let memory_mapping = &mut vm.memory_mapping;
let mut result = ProgramResult::Ok(0);
SyscallAllocFree::call(
invoke_context,
2022-05-20 14:05:54 -07:00
size_of::<T>() as u64,
0,
0,
0,
0,
memory_mapping,
&mut result,
);
let address = result.unwrap();
2020-08-17 09:49:21 -07:00
assert_ne!(address, 0);
assert!(address_is_aligned::<T>(address));
2020-08-17 09:49:21 -07:00
}
aligned::<u8>();
aligned::<u16>();
aligned::<u32>();
aligned::<u64>();
aligned::<u128>();
2020-04-18 17:04:13 -07:00
}
2020-09-29 23:29:20 -07:00
#[test]
fn test_syscall_sha256() {
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader_deprecated::id());
2020-09-29 23:29:20 -07:00
let bytes1 = "Gaggablaghblagh!";
let bytes2 = "flurbos";
let mock_slice1 = MockSlice {
vm_addr: 0x300000000,
2020-09-29 23:29:20 -07:00
len: bytes1.len(),
};
let mock_slice2 = MockSlice {
vm_addr: 0x400000000,
2020-09-29 23:29:20 -07:00
len: bytes2.len(),
};
let bytes_to_hash = [mock_slice1, mock_slice2];
let mut hash_result = [0; HASH_BYTES];
2020-09-29 23:29:20 -07:00
let ro_len = bytes_to_hash.len() as u64;
let ro_va = 0x100000000;
let rw_va = 0x200000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of_slice(&bytes_to_hash), ro_va),
MemoryRegion::new_writable(bytes_of_slice_mut(&mut hash_result), rw_va),
MemoryRegion::new_readonly(bytes1.as_bytes(), bytes_to_hash[0].vm_addr),
MemoryRegion::new_readonly(bytes2.as_bytes(), bytes_to_hash[1].vm_addr),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
(invoke_context.get_compute_budget().sha256_base_cost
+ invoke_context.get_compute_budget().mem_op_base_cost.max(
invoke_context
.get_compute_budget()
.sha256_byte_cost
.saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2),
))
* 4,
);
2020-09-29 23:29:20 -07:00
let mut result = ProgramResult::Ok(0);
SyscallSha256::call(
&mut invoke_context,
ro_va,
ro_len,
rw_va,
0,
0,
&mut memory_mapping,
&mut result,
);
result.unwrap();
2020-09-29 23:29:20 -07:00
let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes();
assert_eq!(hash_result, hash_local);
let mut result = ProgramResult::Ok(0);
SyscallSha256::call(
&mut invoke_context,
ro_va - 1, // AccessViolation
ro_len,
rw_va,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
2020-09-29 23:29:20 -07:00
);
assert_access_violation!(result, ro_va - 1, 32);
let mut result = ProgramResult::Ok(0);
SyscallSha256::call(
&mut invoke_context,
2020-09-29 23:29:20 -07:00
ro_va,
ro_len + 1, // AccessViolation
rw_va,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
2020-09-29 23:29:20 -07:00
);
assert_access_violation!(result, ro_va, 48);
let mut result = ProgramResult::Ok(0);
SyscallSha256::call(
&mut invoke_context,
ro_va,
ro_len,
rw_va - 1, // AccessViolation
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
2020-09-29 23:29:20 -07:00
);
assert_access_violation!(result, rw_va - 1, HASH_BYTES as u64);
let mut result = ProgramResult::Ok(0);
SyscallSha256::call(
&mut invoke_context,
ro_va,
ro_len,
rw_va,
0,
0,
&mut memory_mapping,
&mut result,
2020-09-29 23:29:20 -07:00
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
2020-09-29 23:29:20 -07:00
}
2021-04-12 16:04:57 -07:00
#[test]
fn test_syscall_edwards_curve_point_validation() {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_EDWARDS;
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let valid_bytes: [u8; 32] = [
201, 179, 241, 122, 180, 185, 239, 50, 183, 52, 221, 0, 153, 195, 43, 18, 22, 38, 187,
206, 179, 192, 210, 58, 53, 45, 150, 98, 89, 17, 158, 11,
];
let valid_bytes_va = 0x100000000;
let invalid_bytes: [u8; 32] = [
120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
];
let invalid_bytes_va = 0x200000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_edwards_validate_point_cost)
* 2,
);
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_EDWARDS,
valid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_EDWARDS,
invalid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_EDWARDS,
valid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
}
#[test]
fn test_syscall_ristretto_curve_point_validation() {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_RISTRETTO;
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let valid_bytes: [u8; 32] = [
226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11,
106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118,
];
let valid_bytes_va = 0x100000000;
let invalid_bytes: [u8; 32] = [
120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
];
let invalid_bytes_va = 0x200000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(&valid_bytes, valid_bytes_va),
MemoryRegion::new_readonly(&invalid_bytes, invalid_bytes_va),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_ristretto_validate_point_cost)
* 2,
);
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
valid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
invalid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurvePointValidation::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
valid_bytes_va,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
}
#[test]
fn test_syscall_edwards_curve_group_ops() {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::{
ADD, CURVE25519_EDWARDS, MUL, SUB,
};
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let left_point: [u8; 32] = [
33, 124, 71, 170, 117, 69, 151, 247, 59, 12, 95, 125, 133, 166, 64, 5, 2, 27, 90, 27,
200, 167, 59, 164, 52, 54, 52, 200, 29, 13, 34, 213,
];
let left_point_va = 0x100000000;
let right_point: [u8; 32] = [
70, 222, 137, 221, 253, 204, 71, 51, 78, 8, 124, 1, 67, 200, 102, 225, 122, 228, 111,
183, 129, 14, 131, 210, 212, 95, 109, 246, 55, 10, 159, 91,
];
let right_point_va = 0x200000000;
let scalar: [u8; 32] = [
254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
];
let scalar_va = 0x300000000;
let invalid_point: [u8; 32] = [
120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
];
let invalid_point_va = 0x400000000;
let mut result_point: [u8; 32] = [0; 32];
let result_point_va = 0x500000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_edwards_add_cost
+ invoke_context
.get_compute_budget()
.curve25519_edwards_subtract_cost
+ invoke_context
.get_compute_budget()
.curve25519_edwards_multiply_cost)
* 2,
);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
ADD,
left_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_sum = [
7, 251, 187, 86, 186, 232, 57, 242, 193, 236, 49, 200, 90, 29, 254, 82, 46, 80, 83, 70,
244, 153, 23, 156, 2, 138, 207, 51, 165, 38, 200, 85,
];
assert_eq!(expected_sum, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
ADD,
invalid_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
SUB,
left_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_difference = [
60, 87, 90, 68, 232, 25, 7, 172, 247, 120, 158, 104, 52, 127, 94, 244, 5, 79, 253, 15,
48, 69, 82, 134, 155, 70, 188, 81, 108, 95, 212, 9,
];
assert_eq!(expected_difference, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
SUB,
invalid_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
MUL,
scalar_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
result.unwrap();
let expected_product = [
64, 150, 40, 55, 80, 49, 217, 209, 105, 229, 181, 65, 241, 68, 2, 106, 220, 234, 211,
71, 159, 76, 156, 114, 242, 68, 147, 31, 243, 211, 191, 124,
];
assert_eq!(expected_product, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
MUL,
scalar_va,
invalid_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_EDWARDS,
MUL,
scalar_va,
invalid_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
}
#[test]
fn test_syscall_ristretto_curve_group_ops() {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::{
ADD, CURVE25519_RISTRETTO, MUL, SUB,
};
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let left_point: [u8; 32] = [
208, 165, 125, 204, 2, 100, 218, 17, 170, 194, 23, 9, 102, 156, 134, 136, 217, 190, 98,
34, 183, 194, 228, 153, 92, 11, 108, 103, 28, 57, 88, 15,
];
let left_point_va = 0x100000000;
let right_point: [u8; 32] = [
208, 241, 72, 163, 73, 53, 32, 174, 54, 194, 71, 8, 70, 181, 244, 199, 93, 147, 99,
231, 162, 127, 25, 40, 39, 19, 140, 132, 112, 212, 145, 108,
];
let right_point_va = 0x200000000;
let scalar: [u8; 32] = [
254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
];
let scalar_va = 0x300000000;
let invalid_point: [u8; 32] = [
120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84,
60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79,
];
let invalid_point_va = 0x400000000;
let mut result_point: [u8; 32] = [0; 32];
let result_point_va = 0x500000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of_slice(&left_point), left_point_va),
MemoryRegion::new_readonly(bytes_of_slice(&right_point), right_point_va),
MemoryRegion::new_readonly(bytes_of_slice(&scalar), scalar_va),
MemoryRegion::new_readonly(bytes_of_slice(&invalid_point), invalid_point_va),
MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
(invoke_context
.get_compute_budget()
.curve25519_ristretto_add_cost
+ invoke_context
.get_compute_budget()
.curve25519_ristretto_subtract_cost
+ invoke_context
.get_compute_budget()
.curve25519_ristretto_multiply_cost)
* 2,
);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
ADD,
left_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_sum = [
78, 173, 9, 241, 180, 224, 31, 107, 176, 210, 144, 240, 118, 73, 70, 191, 128, 119,
141, 113, 125, 215, 161, 71, 49, 176, 87, 38, 180, 177, 39, 78,
];
assert_eq!(expected_sum, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
ADD,
invalid_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
SUB,
left_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_difference = [
150, 72, 222, 61, 148, 79, 96, 130, 151, 176, 29, 217, 231, 211, 0, 215, 76, 86, 212,
146, 110, 128, 24, 151, 187, 144, 108, 233, 221, 208, 157, 52,
];
assert_eq!(expected_difference, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
SUB,
invalid_point_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
MUL,
scalar_va,
right_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
result.unwrap();
let expected_product = [
4, 16, 46, 2, 53, 151, 201, 133, 117, 149, 232, 164, 119, 109, 136, 20, 153, 24, 124,
21, 101, 124, 80, 19, 119, 100, 77, 108, 65, 187, 228, 5,
];
assert_eq!(expected_product, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
MUL,
scalar_va,
invalid_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(1, result.unwrap());
let mut result = ProgramResult::Ok(0);
SyscallCurveGroupOps::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
MUL,
scalar_va,
invalid_point_va,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
}
#[test]
fn test_syscall_multiscalar_multiplication() {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::{
CURVE25519_EDWARDS, CURVE25519_RISTRETTO,
};
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let scalar_a: [u8; 32] = [
254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
];
let scalar_b: [u8; 32] = [
254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6,
];
let scalars = [scalar_a, scalar_b];
let scalars_va = 0x100000000;
let edwards_point_x: [u8; 32] = [
252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168,
53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133,
];
let edwards_point_y: [u8; 32] = [
10, 111, 8, 236, 97, 189, 124, 69, 89, 176, 222, 39, 199, 253, 111, 11, 248, 186, 128,
90, 120, 128, 248, 210, 232, 183, 93, 104, 111, 150, 7, 241,
];
let edwards_points = [edwards_point_x, edwards_point_y];
let edwards_points_va = 0x200000000;
let ristretto_point_x: [u8; 32] = [
130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240,
179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112,
];
let ristretto_point_y: [u8; 32] = [
152, 156, 155, 197, 152, 232, 92, 206, 219, 159, 193, 134, 121, 128, 139, 36, 56, 191,
51, 143, 72, 204, 87, 76, 110, 124, 101, 96, 238, 158, 42, 108,
];
let ristretto_points = [ristretto_point_x, ristretto_point_y];
let ristretto_points_va = 0x300000000;
let mut result_point: [u8; 32] = [0; 32];
let result_point_va = 0x400000000;
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of_slice(&scalars), scalars_va),
MemoryRegion::new_readonly(bytes_of_slice(&edwards_points), edwards_points_va),
MemoryRegion::new_readonly(bytes_of_slice(&ristretto_points), ristretto_points_va),
MemoryRegion::new_writable(bytes_of_slice_mut(&mut result_point), result_point_va),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
invoke_context.mock_set_remaining(
invoke_context
.get_compute_budget()
.curve25519_edwards_msm_base_cost
+ invoke_context
.get_compute_budget()
.curve25519_edwards_msm_incremental_cost
+ invoke_context
.get_compute_budget()
.curve25519_ristretto_msm_base_cost
+ invoke_context
.get_compute_budget()
.curve25519_ristretto_msm_incremental_cost,
);
let mut result = ProgramResult::Ok(0);
SyscallCurveMultiscalarMultiplication::call(
&mut invoke_context,
CURVE25519_EDWARDS,
scalars_va,
edwards_points_va,
2,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_product = [
30, 174, 168, 34, 160, 70, 63, 166, 236, 18, 74, 144, 185, 222, 208, 243, 5, 54, 223,
172, 185, 75, 244, 26, 70, 18, 248, 46, 207, 184, 235, 60,
];
assert_eq!(expected_product, result_point);
let mut result = ProgramResult::Ok(0);
SyscallCurveMultiscalarMultiplication::call(
&mut invoke_context,
CURVE25519_RISTRETTO,
scalars_va,
ristretto_points_va,
2,
result_point_va,
&mut memory_mapping,
&mut result,
);
assert_eq!(0, result.unwrap());
let expected_product = [
78, 120, 86, 111, 152, 64, 146, 84, 14, 236, 77, 147, 237, 190, 251, 241, 136, 167, 21,
94, 84, 118, 92, 140, 120, 81, 30, 246, 173, 140, 195, 86,
];
assert_eq!(expected_product, result_point);
}
fn create_filled_type<T: Default>(zero_init: bool) -> T {
let mut val = T::default();
let p = &mut val as *mut _ as *mut u8;
for i in 0..(size_of::<T>() as isize) {
unsafe {
*p.offset(i) = if zero_init { 0 } else { i as u8 };
}
}
val
}
fn are_bytes_equal<T>(first: &T, second: &T) -> bool {
let p_first = first as *const _ as *const u8;
let p_second = second as *const _ as *const u8;
for i in 0..(size_of::<T>() as isize) {
unsafe {
if *p_first.offset(i) != *p_second.offset(i) {
return false;
}
}
}
true
}
2021-04-12 16:04:57 -07:00
#[test]
#[allow(deprecated)]
2021-04-12 16:04:57 -07:00
fn test_syscall_get_sysvar() {
let config = Config::default();
let mut src_clock = create_filled_type::<Clock>(false);
src_clock.slot = 1;
src_clock.epoch_start_timestamp = 2;
src_clock.epoch = 3;
src_clock.leader_schedule_epoch = 4;
src_clock.unix_timestamp = 5;
let mut src_epochschedule = create_filled_type::<EpochSchedule>(false);
src_epochschedule.slots_per_epoch = 1;
src_epochschedule.leader_schedule_slot_offset = 2;
src_epochschedule.warmup = false;
src_epochschedule.first_normal_epoch = 3;
src_epochschedule.first_normal_slot = 4;
let mut src_fees = create_filled_type::<Fees>(false);
src_fees.fee_calculator = FeeCalculator {
lamports_per_signature: 1,
};
let mut src_rent = create_filled_type::<Rent>(false);
src_rent.lamports_per_byte_year = 1;
src_rent.exemption_threshold = 2.0;
src_rent.burn_percent = 3;
let mut src_rewards = create_filled_type::<EpochRewards>(false);
src_rewards.total_rewards = 100;
src_rewards.distributed_rewards = 10;
src_rewards.distribution_complete_block_height = 42;
let mut sysvar_cache = SysvarCache::default();
sysvar_cache.set_clock(src_clock.clone());
sysvar_cache.set_epoch_schedule(src_epochschedule);
sysvar_cache.set_fees(src_fees.clone());
sysvar_cache.set_rent(src_rent);
sysvar_cache.set_epoch_rewards(src_rewards);
let transaction_accounts = vec![
(
sysvar::clock::id(),
create_account_shared_data_for_test(&src_clock),
),
(
sysvar::epoch_schedule::id(),
create_account_shared_data_for_test(&src_epochschedule),
),
(
sysvar::fees::id(),
create_account_shared_data_for_test(&src_fees),
),
(
sysvar::rent::id(),
create_account_shared_data_for_test(&src_rent),
),
(
sysvar::epoch_rewards::id(),
create_account_shared_data_for_test(&src_rewards),
),
];
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
2021-04-12 16:04:57 -07:00
// Test clock sysvar
{
let mut got_clock = Clock::default();
let got_clock_va = 0x100000000;
2021-04-12 16:04:57 -07:00
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(
bytes_of_mut(&mut got_clock),
got_clock_va,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-04-12 16:04:57 -07:00
let mut result = ProgramResult::Ok(0);
SyscallGetClockSysvar::call(
&mut invoke_context,
got_clock_va,
0,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
2021-04-12 16:04:57 -07:00
result.unwrap();
assert_eq!(got_clock, src_clock);
let mut clean_clock = create_filled_type::<Clock>(true);
clean_clock.slot = src_clock.slot;
clean_clock.epoch_start_timestamp = src_clock.epoch_start_timestamp;
clean_clock.epoch = src_clock.epoch;
clean_clock.leader_schedule_epoch = src_clock.leader_schedule_epoch;
clean_clock.unix_timestamp = src_clock.unix_timestamp;
assert!(are_bytes_equal(&got_clock, &clean_clock));
2021-04-12 16:04:57 -07:00
}
// Test epoch_schedule sysvar
{
let mut got_epochschedule = EpochSchedule::default();
let got_epochschedule_va = 0x100000000;
2021-04-12 16:04:57 -07:00
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(
bytes_of_mut(&mut got_epochschedule),
got_epochschedule_va,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-04-12 16:04:57 -07:00
let mut result = ProgramResult::Ok(0);
SyscallGetEpochScheduleSysvar::call(
&mut invoke_context,
2021-04-12 16:04:57 -07:00
got_epochschedule_va,
0,
0,
0,
0,
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
2021-04-12 16:04:57 -07:00
&mut result,
);
result.unwrap();
assert_eq!(got_epochschedule, src_epochschedule);
let mut clean_epochschedule = create_filled_type::<EpochSchedule>(true);
clean_epochschedule.slots_per_epoch = src_epochschedule.slots_per_epoch;
clean_epochschedule.leader_schedule_slot_offset =
src_epochschedule.leader_schedule_slot_offset;
clean_epochschedule.warmup = src_epochschedule.warmup;
clean_epochschedule.first_normal_epoch = src_epochschedule.first_normal_epoch;
clean_epochschedule.first_normal_slot = src_epochschedule.first_normal_slot;
assert!(are_bytes_equal(&got_epochschedule, &clean_epochschedule));
2021-04-12 16:04:57 -07:00
}
// Test fees sysvar
{
let mut got_fees = Fees::default();
let got_fees_va = 0x100000000;
2021-04-12 16:04:57 -07:00
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(
bytes_of_mut(&mut got_fees),
got_fees_va,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-04-12 16:04:57 -07:00
let mut result = ProgramResult::Ok(0);
SyscallGetFeesSysvar::call(
&mut invoke_context,
got_fees_va,
0,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
2021-04-12 16:04:57 -07:00
result.unwrap();
assert_eq!(got_fees, src_fees);
let mut clean_fees = create_filled_type::<Fees>(true);
clean_fees.fee_calculator = src_fees.fee_calculator;
assert!(are_bytes_equal(&got_fees, &clean_fees));
2021-04-12 16:04:57 -07:00
}
// Test rent sysvar
{
let mut got_rent = create_filled_type::<Rent>(true);
let got_rent_va = 0x100000000;
2021-04-12 16:04:57 -07:00
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(
bytes_of_mut(&mut got_rent),
got_rent_va,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
2021-04-12 16:04:57 -07:00
let mut result = ProgramResult::Ok(0);
SyscallGetRentSysvar::call(
&mut invoke_context,
got_rent_va,
0,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
2021-04-12 16:04:57 -07:00
result.unwrap();
assert_eq!(got_rent, src_rent);
let mut clean_rent = create_filled_type::<Rent>(true);
clean_rent.lamports_per_byte_year = src_rent.lamports_per_byte_year;
clean_rent.exemption_threshold = src_rent.exemption_threshold;
clean_rent.burn_percent = src_rent.burn_percent;
assert!(are_bytes_equal(&got_rent, &clean_rent));
2021-04-12 16:04:57 -07:00
}
// Test epoch rewards sysvar
{
let mut got_rewards = create_filled_type::<EpochRewards>(true);
let got_rewards_va = 0x100000000;
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(
bytes_of_mut(&mut got_rewards),
got_rewards_va,
)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let mut result = ProgramResult::Ok(0);
SyscallGetEpochRewardsSysvar::call(
&mut invoke_context,
got_rewards_va,
0,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
result.unwrap();
assert_eq!(got_rewards, src_rewards);
let mut clean_rewards = create_filled_type::<EpochRewards>(true);
clean_rewards.total_rewards = src_rewards.total_rewards;
clean_rewards.distributed_rewards = src_rewards.distributed_rewards;
clean_rewards.distribution_complete_block_height =
src_rewards.distribution_complete_block_height;
assert!(are_bytes_equal(&got_rewards, &clean_rewards));
}
2021-04-12 16:04:57 -07:00
}
2021-08-16 16:16:52 -07:00
fn call_program_address_common<'a, 'b: 'a>(
invoke_context: &'a mut InvokeContext<'b>,
seeds: &[&[u8]],
program_id: &Pubkey,
overlap_outputs: bool,
syscall: BuiltinFunction<InvokeContext<'b>>,
) -> Result<(Pubkey, u8), Error> {
const SEEDS_VA: u64 = 0x100000000;
const PROGRAM_ID_VA: u64 = 0x200000000;
const ADDRESS_VA: u64 = 0x300000000;
const BUMP_SEED_VA: u64 = 0x400000000;
const SEED_VA: u64 = 0x500000000;
let config = Config::default();
let mut address = Pubkey::default();
let mut bump_seed = 0;
let mut regions = vec![
MemoryRegion::new_readonly(bytes_of(program_id), PROGRAM_ID_VA),
MemoryRegion::new_writable(bytes_of_mut(&mut address), ADDRESS_VA),
MemoryRegion::new_writable(bytes_of_mut(&mut bump_seed), BUMP_SEED_VA),
];
let mut mock_slices = Vec::with_capacity(seeds.len());
for (i, seed) in seeds.iter().enumerate() {
2022-03-02 14:50:16 -08:00
let vm_addr = SEED_VA.saturating_add((i as u64).saturating_mul(0x100000000));
let mock_slice = MockSlice {
vm_addr,
len: seed.len(),
};
mock_slices.push(mock_slice);
regions.push(MemoryRegion::new_readonly(bytes_of_slice(seed), vm_addr));
}
regions.push(MemoryRegion::new_readonly(
bytes_of_slice(&mock_slices),
SEEDS_VA,
));
2023-07-05 10:46:21 -07:00
let mut memory_mapping = MemoryMapping::new(regions, &config, &SBPFVersion::V2).unwrap();
let mut result = ProgramResult::Ok(0);
syscall(
invoke_context,
SEEDS_VA,
seeds.len() as u64,
PROGRAM_ID_VA,
ADDRESS_VA,
if overlap_outputs {
ADDRESS_VA
} else {
BUMP_SEED_VA
},
2022-05-19 00:19:05 -07:00
&mut memory_mapping,
&mut result,
);
Result::<u64, Error>::from(result).map(|_| (address, bump_seed))
}
fn create_program_address(
invoke_context: &mut InvokeContext,
seeds: &[&[u8]],
address: &Pubkey,
) -> Result<Pubkey, Error> {
let (address, _) = call_program_address_common(
invoke_context,
seeds,
address,
false,
SyscallCreateProgramAddress::call,
)?;
Ok(address)
}
fn try_find_program_address(
invoke_context: &mut InvokeContext,
seeds: &[&[u8]],
address: &Pubkey,
) -> Result<(Pubkey, u8), Error> {
call_program_address_common(
invoke_context,
seeds,
address,
false,
SyscallTryFindProgramAddress::call,
)
}
#[test]
fn test_set_and_get_return_data() {
const SRC_VA: u64 = 0x100000000;
const DST_VA: u64 = 0x200000000;
const PROGRAM_ID_VA: u64 = 0x300000000;
let data = vec![42; 24];
let mut data_buffer = vec![0; 16];
let mut id_buffer = vec![0; 32];
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(&data, SRC_VA),
MemoryRegion::new_writable(&mut data_buffer, DST_VA),
MemoryRegion::new_writable(&mut id_buffer, PROGRAM_ID_VA),
],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut result = ProgramResult::Ok(0);
SyscallSetReturnData::call(
&mut invoke_context,
SRC_VA,
data.len() as u64,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
let mut result = ProgramResult::Ok(0);
SyscallGetReturnData::call(
&mut invoke_context,
DST_VA,
data_buffer.len() as u64,
PROGRAM_ID_VA,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(result.unwrap() as usize, data.len());
assert_eq!(data.get(0..data_buffer.len()).unwrap(), data_buffer);
assert_eq!(id_buffer, program_id.to_bytes());
let mut result = ProgramResult::Ok(0);
SyscallGetReturnData::call(
&mut invoke_context,
PROGRAM_ID_VA,
data_buffer.len() as u64,
PROGRAM_ID_VA,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
);
}
#[test]
fn test_syscall_sol_get_processed_sibling_instruction() {
let transaction_accounts = (0..9)
.map(|_| {
(
Pubkey::new_unique(),
AccountSharedData::new(0, 0, &bpf_loader::id()),
)
})
.collect::<Vec<_>>();
let instruction_trace = [1, 2, 3, 2, 2, 3, 4, 3];
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
while stack_height
<= invoke_context
.transaction_context
.get_instruction_context_stack_height()
{
invoke_context.transaction_context.pop().unwrap();
}
if stack_height
> invoke_context
.transaction_context
.get_instruction_context_stack_height()
{
let instruction_accounts = [InstructionAccount {
index_in_transaction: index_in_trace.saturating_add(1) as IndexOfAccount,
index_in_caller: 0, // This is incorrect / inconsistent but not required
index_in_callee: 0,
is_signer: false,
is_writable: false,
}];
invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(&[0], &instruction_accounts, &[index_in_trace as u8]);
invoke_context.transaction_context.push().unwrap();
}
}
let syscall_base_cost = invoke_context.get_compute_budget().syscall_base_cost;
const VM_BASE_ADDRESS: u64 = 0x100000000;
const META_OFFSET: usize = 0;
const PROGRAM_ID_OFFSET: usize =
META_OFFSET + std::mem::size_of::<ProcessedSiblingInstruction>();
const DATA_OFFSET: usize = PROGRAM_ID_OFFSET + std::mem::size_of::<Pubkey>();
const ACCOUNTS_OFFSET: usize = DATA_OFFSET + 0x100;
const END_OFFSET: usize = ACCOUNTS_OFFSET + std::mem::size_of::<AccountInfo>() * 4;
let mut memory = [0u8; END_OFFSET];
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
vec![MemoryRegion::new_writable(&mut memory, VM_BASE_ADDRESS)],
&config,
2023-07-05 10:46:21 -07:00
&SBPFVersion::V2,
)
.unwrap();
let processed_sibling_instruction = translate_type_mut::<ProcessedSiblingInstruction>(
&memory_mapping,
VM_BASE_ADDRESS,
true,
)
.unwrap();
processed_sibling_instruction.data_len = 1;
processed_sibling_instruction.accounts_len = 1;
let program_id = translate_type_mut::<Pubkey>(
&memory_mapping,
VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
true,
)
.unwrap();
let data = translate_slice_mut::<u8>(
&memory_mapping,
VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
2022-11-09 11:39:38 -08:00
processed_sibling_instruction.data_len,
true,
true,
)
.unwrap();
let accounts = translate_slice_mut::<AccountMeta>(
&memory_mapping,
VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
2022-11-09 11:39:38 -08:00
processed_sibling_instruction.accounts_len,
true,
true,
)
.unwrap();
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
0,
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
&mut memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 1);
{
let transaction_context = &invoke_context.transaction_context;
assert_eq!(processed_sibling_instruction.data_len, 1);
assert_eq!(processed_sibling_instruction.accounts_len, 1);
assert_eq!(
program_id,
transaction_context.get_key_of_account_at_index(0).unwrap(),
);
assert_eq!(data, &[5]);
assert_eq!(
accounts,
&[AccountMeta {
pubkey: *transaction_context.get_key_of_account_at_index(6).unwrap(),
is_signer: false,
is_writable: false
}]
);
}
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
1,
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(PROGRAM_ID_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(DATA_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(ACCOUNTS_OFFSET as u64),
&mut memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
invoke_context.mock_set_remaining(syscall_base_cost);
let mut result = ProgramResult::Ok(0);
SyscallGetProcessedSiblingInstruction::call(
&mut invoke_context,
0,
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
VM_BASE_ADDRESS.saturating_add(META_OFFSET as u64),
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
);
}
#[test]
fn test_create_program_address() {
// These tests duplicate the direct tests in solana_program::pubkey
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let address = bpf_loader_upgradeable::id();
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
assert_matches!(
create_program_address(&mut invoke_context, &[exceeded_seed], &address),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
);
assert_matches!(
create_program_address(
&mut invoke_context,
&[b"short_seed", exceeded_seed],
&address,
),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
);
let max_seed = &[0; MAX_SEED_LEN];
assert!(create_program_address(&mut invoke_context, &[max_seed], &address).is_ok());
let exceeded_seeds: &[&[u8]] = &[
&[1],
&[2],
&[3],
&[4],
&[5],
&[6],
&[7],
&[8],
&[9],
&[10],
&[11],
&[12],
&[13],
&[14],
&[15],
&[16],
];
assert!(create_program_address(&mut invoke_context, exceeded_seeds, &address).is_ok());
let max_seeds: &[&[u8]] = &[
&[1],
&[2],
&[3],
&[4],
&[5],
&[6],
&[7],
&[8],
&[9],
&[10],
&[11],
&[12],
&[13],
&[14],
&[15],
&[16],
&[17],
];
assert_matches!(
create_program_address(&mut invoke_context, max_seeds, &address),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
);
assert_eq!(
create_program_address(&mut invoke_context, &[b"", &[1]], &address).unwrap(),
"BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe"
.parse()
.unwrap(),
);
assert_eq!(
create_program_address(&mut invoke_context, &["".as_ref(), &[0]], &address).unwrap(),
"13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19"
.parse()
.unwrap(),
);
assert_eq!(
create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
.unwrap(),
"2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk"
.parse()
.unwrap(),
);
let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap();
assert_eq!(
create_program_address(&mut invoke_context, &[public_key.as_ref(), &[1]], &address)
.unwrap(),
"976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL"
.parse()
.unwrap(),
);
assert_ne!(
create_program_address(&mut invoke_context, &[b"Talking", b"Squirrels"], &address)
.unwrap(),
create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(),
);
invoke_context.mock_set_remaining(0);
assert_matches!(
create_program_address(&mut invoke_context, &[b"", &[1]], &address),
Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
}
#[test]
fn test_find_program_address() {
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;
let address = bpf_loader_upgradeable::id();
let max_tries = 256; // one per seed
for _ in 0..1_000 {
let address = Pubkey::new_unique();
invoke_context.mock_set_remaining(cost * max_tries);
let (found_address, bump_seed) =
try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address)
.unwrap();
assert_eq!(
found_address,
create_program_address(
&mut invoke_context,
&[b"Lil'", b"Bits", &[bump_seed]],
&address,
)
.unwrap()
);
}
let seeds: &[&[u8]] = &[b""];
invoke_context.mock_set_remaining(cost * max_tries);
let (_, bump_seed) =
try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64));
try_find_program_address(&mut invoke_context, seeds, &address).unwrap();
invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1));
assert_matches!(
try_find_program_address(&mut invoke_context, seeds, &address),
Result::Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ComputationalBudgetExceeded
);
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
invoke_context.mock_set_remaining(cost * (max_tries - 1));
assert_matches!(
try_find_program_address(&mut invoke_context, &[exceeded_seed], &address),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
);
let exceeded_seeds: &[&[u8]] = &[
&[1],
&[2],
&[3],
&[4],
&[5],
&[6],
&[7],
&[8],
&[9],
&[10],
&[11],
&[12],
&[13],
&[14],
&[15],
&[16],
&[17],
];
invoke_context.mock_set_remaining(cost * (max_tries - 1));
assert_matches!(
try_find_program_address(&mut invoke_context, exceeded_seeds, &address),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded)
);
assert_matches!(
call_program_address_common(
&mut invoke_context,
seeds,
&address,
true,
SyscallTryFindProgramAddress::call,
),
Result::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::CopyOverlapping
);
}
#[test]
fn test_syscall_big_mod_exp() {
let config = Config::default();
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
const VADDR_PARAMS: u64 = 0x100000000;
const MAX_LEN: u64 = 512;
const INV_LEN: u64 = MAX_LEN + 1;
let data: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
const VADDR_DATA: u64 = 0x200000000;
let mut data_out: [u8; INV_LEN as usize] = [0; INV_LEN as usize];
const VADDR_OUT: u64 = 0x300000000;
// Test that SyscallBigModExp succeeds with the maximum param size
{
let params_max_len = BigModExpParams {
base: VADDR_DATA as *const u8,
base_len: MAX_LEN,
exponent: VADDR_DATA as *const u8,
exponent_len: MAX_LEN,
modulus: VADDR_DATA as *const u8,
modulus_len: MAX_LEN,
};
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of(&params_max_len), VADDR_PARAMS),
MemoryRegion::new_readonly(&data, VADDR_DATA),
MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
],
&config,
&SBPFVersion::V2,
)
.unwrap();
let budget = invoke_context.get_compute_budget();
invoke_context.mock_set_remaining(
budget.syscall_base_cost
+ (MAX_LEN * MAX_LEN) / budget.big_modular_exponentiation_cost,
);
let mut result = ProgramResult::Ok(0);
SyscallBigModExp::call(
&mut invoke_context,
VADDR_PARAMS,
VADDR_OUT,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_eq!(result.unwrap(), 0);
}
// Test that SyscallBigModExp fails when the maximum param size is exceeded
{
let params_inv_len = BigModExpParams {
base: VADDR_DATA as *const u8,
base_len: INV_LEN,
exponent: VADDR_DATA as *const u8,
exponent_len: INV_LEN,
modulus: VADDR_DATA as *const u8,
modulus_len: INV_LEN,
};
let mut memory_mapping = MemoryMapping::new(
vec![
MemoryRegion::new_readonly(bytes_of(&params_inv_len), VADDR_PARAMS),
MemoryRegion::new_readonly(&data, VADDR_DATA),
MemoryRegion::new_writable(&mut data_out, VADDR_OUT),
],
&config,
&SBPFVersion::V2,
)
.unwrap();
let budget = invoke_context.get_compute_budget();
invoke_context.mock_set_remaining(
budget.syscall_base_cost
+ (INV_LEN * INV_LEN) / budget.big_modular_exponentiation_cost,
);
let mut result = ProgramResult::Ok(0);
SyscallBigModExp::call(
&mut invoke_context,
VADDR_PARAMS,
VADDR_OUT,
0,
0,
0,
&mut memory_mapping,
&mut result,
);
assert_matches!(
result,
ProgramResult::Err(error) if error.downcast_ref::<SyscallError>().unwrap() == &SyscallError::InvalidLength
);
}
}
#[test]
fn test_check_type_assumptions() {
2022-03-30 08:28:49 -07:00
check_type_assumptions();
}
fn bytes_of<T>(val: &T) -> &[u8] {
let size = mem::size_of::<T>();
unsafe { slice::from_raw_parts(std::slice::from_ref(val).as_ptr().cast(), size) }
}
fn bytes_of_mut<T>(val: &mut T) -> &mut [u8] {
let size = mem::size_of::<T>();
unsafe { slice::from_raw_parts_mut(slice::from_mut(val).as_mut_ptr().cast(), size) }
}
pub fn bytes_of_slice<T>(val: &[T]) -> &[u8] {
let size = val.len().wrapping_mul(mem::size_of::<T>());
unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) }
}
pub fn bytes_of_slice_mut<T>(val: &mut [T]) -> &mut [u8] {
let size = val.len().wrapping_mul(mem::size_of::<T>());
unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) }
}
#[test]
fn test_address_is_aligned() {
for address in 0..std::mem::size_of::<u64>() {
assert_eq!(address_is_aligned::<u64>(address as u64), address == 0);
}
}
2020-04-18 17:04:13 -07:00
}