diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 2318b962e2..4c0a9e4d58 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -178,33 +178,6 @@ extern uint64_t entrypoint(const uint8_t *input) { SOL_ARRAY_SIZE(signers_seeds))); } - sol_log("Test multiple derived signers"); - { - SolAccountMeta arguments[] = { - {accounts[DERIVED_KEY1_INDEX].key, true, false}, - {accounts[DERIVED_KEY2_INDEX].key, true, true}, - {accounts[DERIVED_KEY3_INDEX].key, false, true}}; - uint8_t data[] = {TEST_VERIFY_NESTED_SIGNERS}; - const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; - uint8_t seed1[] = {'L', 'i', 'l', '\''}; - uint8_t seed2[] = {'B', 'i', 't', 's'}; - const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}, - {seed2, SOL_ARRAY_SIZE(seed2)}, - {&nonce2, 1}}; - const SolSignerSeed seeds2[] = { - {(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY}, - {&nonce3, 1}}; - const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, - {seeds2, SOL_ARRAY_SIZE(seeds2)}}; - - sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts, - SOL_ARRAY_SIZE(accounts), - signers_seeds, - SOL_ARRAY_SIZE(signers_seeds))); - } - sol_log("Test readonly with writable account"); { SolAccountMeta arguments[] = { @@ -227,7 +200,8 @@ extern uint64_t entrypoint(const uint8_t *input) { SolAccountMeta arguments[] = { {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, - {accounts[ARGUMENT_INDEX].key, true, true}}; + {accounts[ARGUMENT_INDEX].key, true, true}, + {accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false}}; uint8_t data[] = {TEST_NESTED_INVOKE}; const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), @@ -240,8 +214,9 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_assert(SUCCESS == sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1); - sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == 10 + 5 - 1 - 1); + sol_assert(*accounts[ARGUMENT_INDEX].lamports == 42 - 5 + 1 + 1 + 1 + 1); + sol_assert(*accounts[INVOKED_ARGUMENT_INDEX].lamports == + 10 + 5 - 1 - 1 - 1 - 1); } sol_log("Verify data values are retained and updated"); diff --git a/programs/bpf/c/src/invoked/invoked.c b/programs/bpf/c/src/invoked/invoked.c index b70b71f6de..2e1ef07dae 100644 --- a/programs/bpf/c/src/invoked/invoked.c +++ b/programs/bpf/c/src/invoked/invoked.c @@ -101,6 +101,32 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer); sol_assert(!accounts[DERIVED_KEY2_INDEX].is_signer); + uint8_t nonce2 = params.data[1]; + uint8_t nonce3 = params.data[2]; + + SolAccountMeta arguments[] = { + {accounts[DERIVED_KEY1_INDEX].key, true, false}, + {accounts[DERIVED_KEY2_INDEX].key, true, true}, + {accounts[DERIVED_KEY3_INDEX].key, false, true}}; + uint8_t data[] = {TEST_VERIFY_NESTED_SIGNERS}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + uint8_t seed1[] = {'L', 'i', 'l', '\''}; + uint8_t seed2[] = {'B', 'i', 't', 's'}; + const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}, + {seed2, SOL_ARRAY_SIZE(seed2)}, + {&nonce2, 1}}; + const SolSignerSeed seeds2[] = { + {(uint8_t *)accounts[DERIVED_KEY2_INDEX].key, SIZE_PUBKEY}, + {&nonce3, 1}}; + const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, + {seeds2, SOL_ARRAY_SIZE(seeds2)}}; + + sol_assert(SUCCESS == sol_invoke_signed( + &instruction, accounts, SOL_ARRAY_SIZE(accounts), + signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); + break; } @@ -114,6 +140,7 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_assert(!accounts[DERIVED_KEY1_INDEX].is_signer); sol_assert(accounts[DERIVED_KEY2_INDEX].is_signer); sol_assert(accounts[DERIVED_KEY2_INDEX].is_signer); + break; } @@ -133,6 +160,12 @@ extern uint64_t entrypoint(const uint8_t *input) { static const int INVOKED_ARGUMENT_INDEX = 0; static const int ARGUMENT_INDEX = 1; + static const int INVOKED_PROGRAM_INDEX = 2; + + if (!sol_deserialize(input, ¶ms, 3)) { + sol_assert(sol_deserialize(input, ¶ms, 2)); + } + sol_assert(sol_deserialize(input, ¶ms, 2)); sol_assert(accounts[INVOKED_ARGUMENT_INDEX].is_signer); @@ -141,9 +174,23 @@ extern uint64_t entrypoint(const uint8_t *input) { *accounts[INVOKED_ARGUMENT_INDEX].lamports -= 1; *accounts[ARGUMENT_INDEX].lamports += 1; - sol_log("Last invoke"); - for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) { - accounts[INVOKED_ARGUMENT_INDEX].data[i] = i; + if (params.ka_num == 3) { + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, true, true}, + {accounts[ARGUMENT_INDEX].key, true, true}}; + uint8_t data[] = {TEST_NESTED_INVOKE}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_log("Invoke again"); + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + } else { + sol_log("Last invoked"); + for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) { + accounts[INVOKED_ARGUMENT_INDEX].data[i] = i; + } } break; } diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index eac71a6d09..68d5a23172 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -174,24 +174,6 @@ fn process_instruction( accounts, &[&[b"You pass butter", &[nonce1]]], )?; - - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[DERIVED_KEY1_INDEX].key, true, false), - (accounts[DERIVED_KEY2_INDEX].key, true, true), - (accounts[DERIVED_KEY3_INDEX].key, false, true), - ], - vec![TEST_VERIFY_NESTED_SIGNERS], - ); - invoke_signed( - &invoked_instruction, - accounts, - &[ - &[b"Lil'", b"Bits", &[nonce2]], - &[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[nonce3]], - ], - )?; } info!("Test readonly with writable account"); @@ -217,6 +199,8 @@ fn process_instruction( &[ (accounts[ARGUMENT_INDEX].key, true, true), (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), ], vec![TEST_NESTED_INVOKE], ); @@ -224,9 +208,11 @@ fn process_instruction( info!("2nd invoke from first program"); invoke(&instruction, accounts)?; - info!(line!(), 0, 0, 0, accounts[ARGUMENT_INDEX].lamports()); - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1); - assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].lamports(), 10 + 5 - 1 - 1); + assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42 - 5 + 1 + 1 + 1 + 1); + assert_eq!( + accounts[INVOKED_ARGUMENT_INDEX].lamports(), + 10 + 5 - 1 - 1 - 1 - 1 + ); } info!("Verify data values are retained and updated"); diff --git a/programs/bpf/rust/invoked/src/processor.rs b/programs/bpf/rust/invoked/src/processor.rs index f926061ed0..ec04a56b30 100644 --- a/programs/bpf/rust/invoked/src/processor.rs +++ b/programs/bpf/rust/invoked/src/processor.rs @@ -1,10 +1,14 @@ //! @brief Example Rust-based BPF program that issues a cross-program-invocation use crate::instruction::*; -use solana_sdk::entrypoint; use solana_sdk::{ - account_info::AccountInfo, bpf_loader, entrypoint::ProgramResult, info, program::invoke, - program_error::ProgramError, pubkey::Pubkey, + account_info::AccountInfo, + bpf_loader, entrypoint, + entrypoint::ProgramResult, + info, + program::{invoke, invoke_signed}, + program_error::ProgramError, + pubkey::Pubkey, }; entrypoint!(process_instruction); @@ -105,6 +109,7 @@ fn process_instruction( } TEST_DERIVED_SIGNERS => { info!("verify derived signers"); + const INVOKED_PROGRAM_INDEX: usize = 0; const DERIVED_KEY1_INDEX: usize = 1; const DERIVED_KEY2_INDEX: usize = 2; const DERIVED_KEY3_INDEX: usize = 3; @@ -112,6 +117,26 @@ fn process_instruction( assert!(accounts[DERIVED_KEY1_INDEX].is_signer); assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); + + let nonce2 = instruction_data[1]; + let nonce3 = instruction_data[2]; + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[DERIVED_KEY1_INDEX].key, true, false), + (accounts[DERIVED_KEY2_INDEX].key, true, true), + (accounts[DERIVED_KEY3_INDEX].key, false, true), + ], + vec![TEST_VERIFY_NESTED_SIGNERS], + ); + invoke_signed( + &invoked_instruction, + accounts, + &[ + &[b"Lil'", b"Bits", &[nonce2]], + &[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[nonce3]], + ], + )?; } TEST_VERIFY_NESTED_SIGNERS => { info!("verify nested derived signers"); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index bec523d38a..fee5809cba 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -483,9 +483,9 @@ fn test_program_bpf_invoke() { let (derived_key1, nonce1) = Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id); let (derived_key2, nonce2) = - Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoke_program_id); + Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id); let (derived_key3, nonce3) = - Pubkey::find_program_address(&[derived_key2.as_ref()], &invoke_program_id); + Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id); let mint_pubkey = mint_keypair.pubkey(); let account_metas = vec![ @@ -539,6 +539,8 @@ fn test_program_bpf_invoke() { invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), + invoked_program_id.clone(), + invoked_program_id.clone(), ] ); diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index b44fddab08..d7949b7157 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -17,7 +17,7 @@ use solana_rbpf::{ vm::{EbpfVm, Executable, InstructionMeter}, }; use solana_runtime::{ - feature_set::compute_budget_config2, + feature_set::compute_budget_balancing, process_instruction::{ComputeMeter, Executor, InvokeContext}, }; use solana_sdk::{ @@ -101,7 +101,7 @@ pub fn create_and_cache_executor( .map_err(|e| map_ebpf_error(invoke_context, e))?; bpf_verifier::check( elf_bytes, - !invoke_context.is_feature_active(&compute_budget_config2::id()), + !invoke_context.is_feature_active(&compute_budget_balancing::id()), ) .map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?; let executor = Arc::new(BPFExecutor { executable }); diff --git a/runtime/src/feature_set.rs b/runtime/src/feature_set.rs index e6d1f99559..6179025333 100644 --- a/runtime/src/feature_set.rs +++ b/runtime/src/feature_set.rs @@ -33,7 +33,7 @@ pub mod bpf_loader2_program { solana_sdk::declare_id!("DFBnrgThdzH4W6wZ12uGPoWcMnvfZj11EHnxHcVxLPhD"); } -pub mod compute_budget_config2 { +pub mod compute_budget_balancing { solana_sdk::declare_id!("HxvjqDSiF5sYdSYuCXsUnS8UeAoWsMT9iGoFP8pgV1mB"); } @@ -49,6 +49,10 @@ pub mod ristretto_mul_syscall_enabled { solana_sdk::declare_id!("HRe7A6aoxgjKzdjbBv6HTy7tJ4YWqE6tVmYCGho6S9Aq"); } +pub mod max_invoke_depth_4 { + solana_sdk::declare_id!("EdM9xggY5y7AhNMskRG8NgGMnaP4JFNsWi8ZZtyT1af5"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -59,10 +63,11 @@ lazy_static! { (inflation_kill_switch::id(), "inflation kill switch"), (spl_token_v2_multisig_fix::id(), "spl-token multisig fix"), (bpf_loader2_program::id(), "bpf_loader2 program"), - (compute_budget_config2::id(), "1ms compute budget"), + (compute_budget_balancing::id(), "compute budget balancing"), (sha256_syscall_enabled::id(), "sha256 syscall"), (no_overflow_rent_distribution::id(), "no overflow rent distribution"), (ristretto_mul_syscall_enabled::id(), "ristretto multiply syscall"), + (max_invoke_depth_4::id(), "max invoke call depth 4"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 2f8e667f26..05730239a0 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -1,5 +1,5 @@ use crate::{ - feature_set::{compute_budget_config2, instructions_sysvar_enabled, FeatureSet}, + feature_set::{instructions_sysvar_enabled, FeatureSet}, instruction_recorder::InstructionRecorder, log_collector::LogCollector, native_loader::NativeLoader, @@ -244,7 +244,7 @@ impl ThisInvokeContext { } impl InvokeContext for ThisInvokeContext { fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> { - if self.program_ids.len() >= self.compute_budget.max_invoke_depth { + if self.program_ids.len() > self.compute_budget.max_invoke_depth { return Err(InstructionError::CallDepth); } if self.program_ids.contains(key) && self.program_ids.last() != Some(key) { @@ -416,21 +416,7 @@ impl MessageProcessor { } fn get_compute_budget(feature_set: &FeatureSet) -> ComputeBudget { - if feature_set.is_active(&compute_budget_config2::id()) { - ComputeBudget::default() - } else { - // Original - ComputeBudget { - max_units: 100_000, - log_units: 0, - log_64_units: 0, - create_program_address_units: 0, - invoke_units: 0, - max_invoke_depth: 2, - sha256_base_cost: 0, - sha256_byte_cost: 0, - } - } + ComputeBudget::new(feature_set) } /// Create the KeyedAccounts that will be passed to the program diff --git a/runtime/src/process_instruction.rs b/runtime/src/process_instruction.rs index 1890852bfc..2eec69ac3a 100644 --- a/runtime/src/process_instruction.rs +++ b/runtime/src/process_instruction.rs @@ -1,3 +1,4 @@ +use crate::feature_set::{compute_budget_balancing, max_invoke_depth_4, FeatureSet}; use solana_sdk::{ account::{Account, KeyedAccount}, instruction::{CompiledInstruction, Instruction, InstructionError}, @@ -94,17 +95,42 @@ pub struct ComputeBudget { } impl Default for ComputeBudget { fn default() -> Self { - // Tuned for ~1ms + Self::new(&FeatureSet::all_enabled()) + } +} +impl ComputeBudget { + pub fn new(feature_set: &FeatureSet) -> Self { + let mut compute_budget = + // Original ComputeBudget { - max_units: 200_000, - log_units: 100, - log_64_units: 100, - create_program_address_units: 1500, - invoke_units: 1000, - max_invoke_depth: 2, + max_units: 100_000, + log_units: 0, + log_64_units: 0, + create_program_address_units: 0, + invoke_units: 0, + max_invoke_depth: 1, sha256_base_cost: 85, sha256_byte_cost: 1, + }; + + if feature_set.is_active(&compute_budget_balancing::id()) { + compute_budget = ComputeBudget { + max_units: 200_000, + log_units: 100, + log_64_units: 100, + create_program_address_units: 1500, + invoke_units: 1000, + ..compute_budget + }; } + if feature_set.is_active(&max_invoke_depth_4::id()) { + compute_budget = ComputeBudget { + max_invoke_depth: 4, + ..compute_budget + } + } + + compute_budget } }