diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index d149f3e41b..b46ab66398 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -1119,6 +1119,7 @@ mod tests { stack_frame_size: 4096, log_pubkey_units: 100, max_cpi_instruction_size: usize::MAX, + cpi_bytes_per_unit: 250, }, Rc::new(RefCell::new(Executors::default())), None, diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index c0291c1c4d..a75bdae17d 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -17,7 +17,7 @@ use solana_sdk::{ bpf_loader_upgradeable::{self, UpgradeableLoaderState}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ - abort_on_all_cpi_failures, limit_cpi_loader_invoke, per_byte_logging_cost, + abort_on_all_cpi_failures, cpi_data_cost, limit_cpi_loader_invoke, per_byte_logging_cost, pubkey_log_syscall_enabled, ristretto_mul_syscall_enabled, sha256_syscall_enabled, sol_log_compute_units_syscall, try_find_program_address_syscall_enabled, use_loaded_executables, use_loaded_program_accounts, @@ -989,7 +989,8 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { }) .collect::, EbpfError>>()?; - let translate = |account_info: &AccountInfo| { + let translate = |account_info: &AccountInfo, + invoke_context: &Ref<&mut dyn InvokeContext>| { // Translate the account from user space let lamports = { @@ -1006,6 +1007,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { account_info.owner as *const _ as u64, self.loader_id, )?; + let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { // Double translate data out of RefCell let data = *translate_type::<&[u8]>( @@ -1013,6 +1015,14 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { account_info.data.as_ptr() as *const _ as u64, self.loader_id, )?; + + if invoke_context.is_feature_active(&cpi_data_cost::id()) { + invoke_context.get_compute_meter().consume( + data.len() as u64 + / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + )?; + } + let translated = translate( memory_mapping, AccessType::Store, @@ -1283,7 +1293,8 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { }) .collect::, EbpfError>>()?; - let translate = |account_info: &SolAccountInfo| { + let translate = |account_info: &SolAccountInfo, + invoke_context: &Ref<&mut dyn InvokeContext>| { // Translate the account from user space let lamports = translate_type_mut::( @@ -1297,6 +1308,14 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { self.loader_id, )?; let vm_data_addr = account_info.data_addr; + + if invoke_context.is_feature_active(&cpi_data_cost::id()) { + invoke_context.get_compute_meter().consume( + account_info.data_len + / invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + )?; + } + let data = translate_slice_mut::( memory_mapping, vm_data_addr, @@ -1436,7 +1455,7 @@ fn get_translated_accounts<'a, T, F>( do_translate: F, ) -> Result, EbpfError> where - F: Fn(&T) -> Result, EbpfError>, + F: Fn(&T, &Ref<&mut dyn InvokeContext>) -> Result, EbpfError>, { let mut accounts = Vec::with_capacity(account_keys.len()); let mut refs = Vec::with_capacity(account_keys.len()); @@ -1470,7 +1489,7 @@ where } }) { - let (account, account_ref) = do_translate(account_info)?; + let (account, account_ref) = do_translate(account_info, invoke_context)?; accounts.push(account); refs.push(account_ref); } else { diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 67d20993ef..bc4295da2c 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -171,6 +171,10 @@ pub mod check_program_owner { solana_sdk::declare_id!("5XnbR5Es9YXEARRuP6mdvoxiW3hx5atNNeBmwVd8P3QD"); } +pub mod cpi_data_cost { + solana_sdk::declare_id!("Hrg5bXePPGiAVWZfDHbvjqytSeyBDPAGAQ7v6N5i4gCX"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -213,7 +217,8 @@ lazy_static! { (warp_timestamp_again::id(), "warp timestamp again, adjust bounding to 25% fast 80% slow #15204"), (per_byte_logging_cost::id(), "charge the compute budget per byte for logging"), (check_init_vote_data::id(), "check initialized Vote data"), - (check_program_owner::id(), "limit programs to operating on accounts owned by itself") + (check_program_owner::id(), "limit programs to operating on accounts owned by itself"), + (cpi_data_cost::id(), "charge the compute budger for data passed via CPI"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index b0e93fd8c9..3e6063145d 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -124,6 +124,8 @@ pub struct BpfComputeBudget { pub log_pubkey_units: u64, /// Maximum cross-program invocation instruction size pub max_cpi_instruction_size: usize, + /// Number of account data bytes per conpute unit charged during a cross-program invocation + pub cpi_bytes_per_unit: u64, } impl Default for BpfComputeBudget { fn default() -> Self { @@ -147,6 +149,7 @@ impl BpfComputeBudget { stack_frame_size: 4_096, log_pubkey_units: 0, max_cpi_instruction_size: std::usize::MAX, + cpi_bytes_per_unit: 250, }; if feature_set.is_active(&bpf_compute_budget_balancing::id()) {