瀏覽代碼

SIMD-0339: Increase CPI Account Infos Limit (#8513)

* SIMD 0339 functionality + integration tests

* refactor feature gate to be in ComputeBudget

* adding types for byte calculation and updated the comment

* fix missed invocation with new function signature

* changed feature name to increase_cpi_account_info_limit

* test renaming for new normal prospective + whitespace format

* changing missed hardcoded value to type size on c account translation

* fixing CI issue with whitespaces in C program

* adding execution cost initialization to bank creation

* adding CU tests

* cargo fmt didn't work on some files

* c file whitespace

* using test-v3 for CU estimations

* upper bound of CU test due to different sbpf versions

* panic error fix

* constant for AccountInfo type size + account info charging relocation

* merge CU consuming calls into 1

* cargo fmt

* cargo fmt the merge

* renamed tests + cargo.lock update

* updated keypair for feature gate + refactored total_cu_translation_cost to match SIMDs expected behaviour

* fmt after upstream sync

* var renaming
Nikita Belenkov 3 周之前
父節點
當前提交
f12e46b798

+ 2 - 2
compute-budget/src/compute_budget.rs

@@ -128,10 +128,10 @@ impl Default for ComputeBudget {
 }
 
 impl ComputeBudget {
-    pub fn new_with_defaults(simd_0268_active: bool) -> Self {
+    pub fn new_with_defaults(simd_0268_active: bool, simd_0339_active: bool) -> Self {
         Self::from_budget_and_cost(
             &SVMTransactionExecutionBudget::new_with_defaults(simd_0268_active),
-            &SVMTransactionExecutionCost::default(),
+            &SVMTransactionExecutionCost::new_with_defaults(simd_0339_active),
         )
     }
 

+ 9 - 0
feature-set/src/lib.rs

@@ -168,6 +168,7 @@ impl FeatureSet {
             raise_cpi_nesting_limit_to_8: self.is_active(&raise_cpi_nesting_limit_to_8::id()),
             provide_instruction_data_offset_in_vm_r2: self
                 .is_active(&provide_instruction_data_offset_in_vm_r2::id()),
+            increase_cpi_account_info_limit: self.is_active(&increase_cpi_account_info_limit::id()),
             vote_state_v4: self.is_active(&vote_state_v4::id()),
         }
     }
@@ -1167,6 +1168,10 @@ pub mod switch_to_chacha8_turbine {
     solana_pubkey::declare_id!("CHaChatUnR3s6cPyPMMGNJa3VdQQ8PNH2JqdD4LpCKnB");
 }
 
+pub mod increase_cpi_account_info_limit {
+    solana_pubkey::declare_id!("H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5");
+}
+
 pub mod deprecate_rent_exemption_threshold {
     solana_pubkey::declare_id!("rent6iVy6PDoViPBeJ6k5EJQrkj62h7DPyLbWGHwjrC");
 }
@@ -2102,6 +2107,10 @@ pub static FEATURE_NAMES: LazyLock<AHashMap<Pubkey, &'static str>> = LazyLock::n
             switch_to_chacha8_turbine::id(),
             "SIMD-0332: Reduce ChaCha rounds for Turbine from 20 to 8",
         ),
+        (
+            increase_cpi_account_info_limit::id(),
+            "SIMD-0339: Increase CPI Account Infos Limit",
+        ),
         (
             deprecate_rent_exemption_threshold::id(),
             "SIMD-0194: Deprecate rent exemption threshold",

+ 71 - 18
program-runtime/src/cpi.rs

@@ -60,6 +60,12 @@ type Error = Box<dyn std::error::Error>;
 const SUCCESS: u64 = 0;
 /// Maximum signers
 const MAX_SIGNERS: usize = 16;
+///SIMD-0339 based calculation of AccountInfo translation byte size. Fixed size of **80 bytes** for each AccountInfo broken down as:
+/// - 32 bytes for account address
+/// - 32 bytes for owner address
+/// - 8 bytes for lamport balance
+/// - 8 bytes for data length
+const ACCOUNT_INFO_BYTE_SIZE: usize = 80;
 
 /// Rust representation of C's SolInstruction
 #[derive(Debug)]
@@ -114,6 +120,8 @@ struct SolSignerSeedsC {
 
 /// Maximum number of account info structs that can be used in a single CPI invocation
 const MAX_CPI_ACCOUNT_INFOS: usize = 128;
+/// Maximum number of account info structs that can be used in a single CPI invocation with SIMD-0339 active
+const MAX_CPI_ACCOUNT_INFOS_SIMD_0339: usize = 255;
 
 /// Check that an account info pointer field points to the expected address
 fn check_account_info_pointer(
@@ -158,6 +166,11 @@ fn check_account_infos(
     invoke_context: &mut InvokeContext,
 ) -> Result<(), Error> {
     let max_cpi_account_infos = if invoke_context
+        .get_feature_set()
+        .increase_cpi_account_info_limit
+    {
+        MAX_CPI_ACCOUNT_INFOS_SIMD_0339
+    } else if invoke_context
         .get_feature_set()
         .increase_tx_account_lock_limit
     {
@@ -531,17 +544,29 @@ pub fn translate_instruction_rust(
         ix.data.as_vaddr(),
         ix.data.len(),
         check_aligned,
-    )?
-    .to_vec();
+    )?;
 
     check_instruction_size(account_metas.len(), data.len())?;
 
-    consume_compute_meter(
-        invoke_context,
-        (data.len() as u64)
-            .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
-            .unwrap_or(u64::MAX),
-    )?;
+    let mut total_cu_translation_cost: u64 = (data.len() as u64)
+        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
+        .unwrap_or(u64::MAX);
+
+    if invoke_context
+        .get_feature_set()
+        .increase_cpi_account_info_limit
+    {
+        // Each account meta is 34 bytes (32 for pubkey, 1 for is_signer, 1 for is_writable)
+        let account_meta_translation_cost =
+            (account_metas.len().saturating_mul(size_of::<AccountMeta>()) as u64)
+                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
+                .unwrap_or(u64::MAX);
+
+        total_cu_translation_cost =
+            total_cu_translation_cost.saturating_add(account_meta_translation_cost);
+    }
+
+    consume_compute_meter(invoke_context, total_cu_translation_cost)?;
 
     let mut accounts = Vec::with_capacity(account_metas.len());
     #[allow(clippy::needless_range_loop)]
@@ -559,7 +584,7 @@ pub fn translate_instruction_rust(
 
     Ok(Instruction {
         accounts,
-        data,
+        data: data.to_vec(),
         program_id: ix.program_id,
     })
 }
@@ -650,17 +675,30 @@ pub fn translate_instruction_c(
         ix_c.accounts_len,
         check_aligned,
     )?;
-    let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?
-        .to_vec();
+    let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
 
     check_instruction_size(ix_c.accounts_len as usize, data.len())?;
 
-    consume_compute_meter(
-        invoke_context,
-        (data.len() as u64)
-            .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
-            .unwrap_or(u64::MAX),
-    )?;
+    let mut total_cu_translation_cost: u64 = (data.len() as u64)
+        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
+        .unwrap_or(u64::MAX);
+
+    if invoke_context
+        .get_feature_set()
+        .increase_cpi_account_info_limit
+    {
+        // Each account meta is 34 bytes (32 for pubkey, 1 for is_signer, 1 for is_writable)
+        let account_meta_translation_cost = (ix_c
+            .accounts_len
+            .saturating_mul(size_of::<AccountMeta>() as u64))
+        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
+        .unwrap_or(u64::MAX);
+
+        total_cu_translation_cost =
+            total_cu_translation_cost.saturating_add(account_meta_translation_cost);
+    }
+
+    consume_compute_meter(invoke_context, total_cu_translation_cost)?;
 
     let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
     #[allow(clippy::needless_range_loop)]
@@ -684,7 +722,7 @@ pub fn translate_instruction_c(
 
     Ok(Instruction {
         accounts,
-        data,
+        data: data.to_vec(),
         program_id: *program_id,
     })
 }
@@ -926,6 +964,21 @@ where
         check_aligned,
     )?;
     check_account_infos(account_infos.len(), invoke_context)?;
+
+    if invoke_context
+        .get_feature_set()
+        .increase_cpi_account_info_limit
+    {
+        let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
+
+        consume_compute_meter(
+            invoke_context,
+            (account_infos_bytes as u64)
+                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
+                .unwrap_or(u64::MAX),
+        )?;
+    }
+
     let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
     #[allow(clippy::needless_range_loop)]
     for account_index in 0..account_infos_len as usize {

+ 21 - 4
program-runtime/src/execution_budget.rs

@@ -17,6 +17,19 @@ fn get_max_instruction_stack_depth(simd_0268_active: bool) -> usize {
     }
 }
 
+//Default CPI invocation cost
+pub const DEFAULT_INVOCATION_COST: u64 = 1000;
+//CPI Invocation cost with SIMD-0339 active
+pub const INVOKE_UNITS_COST_SIMD_0339: u64 = 946;
+
+fn get_invoke_unit_cost(simd_0339_active: bool) -> u64 {
+    if simd_0339_active {
+        INVOKE_UNITS_COST_SIMD_0339
+    } else {
+        DEFAULT_INVOCATION_COST
+    }
+}
+
 /// Max call depth. This is the maximum nesting of SBF to SBF call that can happen within a program.
 pub const MAX_CALL_DEPTH: usize = 64;
 
@@ -176,10 +189,16 @@ pub struct SVMTransactionExecutionCost {
 
 impl Default for SVMTransactionExecutionCost {
     fn default() -> Self {
-        Self {
+        Self::new_with_defaults(/* simd_0339_active */ false)
+    }
+}
+
+impl SVMTransactionExecutionCost {
+    pub fn new_with_defaults(simd_0339_active: bool) -> Self {
+        SVMTransactionExecutionCost {
             log_64_units: 100,
             create_program_address_units: 1500,
-            invoke_units: 1000,
+            invoke_units: get_invoke_unit_cost(simd_0339_active),
             sha256_base_cost: 85,
             sha256_byte_cost: 1,
             log_pubkey_units: 100,
@@ -216,9 +235,7 @@ impl Default for SVMTransactionExecutionCost {
             alt_bn128_g2_decompress: 13610,
         }
     }
-}
 
-impl SVMTransactionExecutionCost {
     /// Returns cost of the Poseidon hash function for the given number of
     /// inputs is determined by the following quadratic function:
     ///

+ 3 - 1
program-runtime/src/invoke_context.rs

@@ -833,7 +833,9 @@ macro_rules! with_mock_invoke_context_with_feature_set {
             environment_config,
             Some(LogCollector::new_ref()),
             compute_budget,
-            SVMTransactionExecutionCost::default(),
+            SVMTransactionExecutionCost::new_with_defaults(
+                $feature_set.increase_cpi_account_info_limit,
+            ),
         );
     };
 }

+ 6 - 1
program-test/src/lib.rs

@@ -13,7 +13,9 @@
 // Export tokio for test clients
 pub use tokio;
 use {
-    agave_feature_set::{raise_cpi_nesting_limit_to_8, FEATURE_NAMES},
+    agave_feature_set::{
+        increase_cpi_account_info_limit, raise_cpi_nesting_limit_to_8, FEATURE_NAMES,
+    },
     async_trait::async_trait,
     base64::{prelude::BASE64_STANDARD, Engine},
     chrono_humanize::{Accuracy, HumanTime, Tense},
@@ -859,6 +861,9 @@ impl ProgramTest {
                         genesis_config
                             .accounts
                             .contains_key(&raise_cpi_nesting_limit_to_8::id()),
+                        genesis_config
+                            .accounts
+                            .contains_key(&increase_cpi_account_info_limit::id()),
                     )
                 }),
                 transaction_account_lock_limit: self.transaction_account_lock_limit,

+ 214 - 3
programs/sbf/c/src/invoke/invoke.c

@@ -9,6 +9,7 @@
 #include <sol/assert.h>
 #include <sol/deserialize.h>
 #include <sol/return_data.h>
+#include <solana_sdk.h>
 
 static const uint8_t TEST_SUCCESS = 1;
 static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2;
@@ -31,7 +32,7 @@ static const uint8_t ADD_LAMPORTS = 18;
 static const uint8_t TEST_RETURN_DATA_TOO_LARGE = 19;
 static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER = 20;
 static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE = 21;
-static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED = 22;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_SIMD_0339 = 22;
 // TEST_CPI_INVALID_* must match the definitions in
 // https://github.com/solana-labs/solana/blob/master/programs/sbf/rust/invoke/src/instructions.rs
 static const uint8_t TEST_CPI_INVALID_KEY_POINTER = 35;
@@ -42,6 +43,14 @@ static const uint8_t TEST_WRITE_ACCOUNT = 40;
 static const uint8_t TEST_ACCOUNT_INFO_IN_ACCOUNT = 43;
 static const uint8_t TEST_NESTED_INVOKE_SIMD_0268_OK = 46;
 static const uint8_t TEST_NESTED_INVOKE_SIMD_0268_TOO_DEEP = 47;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_OK = 48;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED = 49;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_SIMD_0339 = 50;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339 = 51;
+static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339 = 52;
+static const uint8_t TEST_CU_USAGE_MINIMUM = 53;
+static const uint8_t TEST_CU_USAGE_BASELINE = 54;
+static const uint8_t TEST_CU_USAGE_MAX = 55;
 
 static const int MINT_INDEX = 0;
 static const int ARGUMENT_INDEX = 1;
@@ -57,6 +66,7 @@ static const int FROM_INDEX = 10;
 static const int ED25519_PROGRAM_INDEX = 11;
 static const int INVOKE_PROGRAM_INDEX = 12;
 static const int UNEXECUTABLE_PROGRAM_INDEX = 13;
+static const int NOOP_PROGRAM_INDEX = 14;
 
 uint64_t do_nested_invokes(uint64_t num_nested_invokes,
                            SolAccountInfo *accounts, uint64_t num_accounts) {
@@ -90,7 +100,7 @@ uint64_t do_nested_invokes(uint64_t num_nested_invokes,
 extern uint64_t entrypoint(const uint8_t *input) {
   sol_log("invoke C program");
 
-  SolAccountInfo accounts[14];
+  SolAccountInfo accounts[15];
   SolParameters params = (SolParameters){.ka = accounts};
 
   if (!sol_deserialize(input, &params, SOL_ARRAY_SIZE(accounts))) {
@@ -548,10 +558,104 @@ extern uint64_t entrypoint(const uint8_t *input) {
 
     break;
   }
+  case TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339: {
+    sol_log("Test max account infos ok before SIMD-0339 and before increase cpi info");
+    SolAccountMeta arguments[] = {};
+    uint64_t account_infos_len = 64;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    for (uint64_t i = 0; i < account_infos_len; i++) {
+      account_infos[i] = accounts[0];
+    }
+    uint8_t data[] = {};
+    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
+                                        arguments, SOL_ARRAY_SIZE(arguments),
+                                        data, SOL_ARRAY_SIZE(data)};
+    const SolSignerSeeds signers_seeds[] = {};
+    sol_assert(SUCCESS == sol_invoke_signed(
+                              &instruction, account_infos, account_infos_len,
+                              signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    break;
+  }
+  case TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339: {
+    sol_log("Test max account infos exceeded before SIMD-0339 and before increase cpi info");
+    SolAccountMeta arguments[] = {};
+    uint64_t account_infos_len = 65;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    uint8_t data[] = {};
+    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
+                                        arguments, SOL_ARRAY_SIZE(arguments),
+                                        data, SOL_ARRAY_SIZE(data)};
+    const SolSignerSeeds signers_seeds[] = {};
+    sol_assert(SUCCESS == sol_invoke_signed(
+                              &instruction, account_infos, account_infos_len,
+                              signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    break;
+  }
+  case TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_SIMD_0339: {
+    sol_log("Test max account infos ok before SIMD-0339");
+    SolAccountMeta arguments[] = {};
+    uint64_t account_infos_len = MAX_CPI_ACCOUNT_INFOS;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+     for (uint64_t i = 0; i < account_infos_len; i++) {
+      account_infos[i] = accounts[0];
+    }
+    uint8_t data[] = {};
+    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
+                                        arguments, SOL_ARRAY_SIZE(arguments),
+                                        data, SOL_ARRAY_SIZE(data)};
+    const SolSignerSeeds signers_seeds[] = {};
+    sol_assert(SUCCESS == sol_invoke_signed(
+                              &instruction, account_infos, account_infos_len,
+                              signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    break;
+  }
+  case TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_SIMD_0339: {
+    sol_log("Test max account infos exceeded before SIMD-0339");
+    SolAccountMeta arguments[] = {};
+    uint64_t account_infos_len = MAX_CPI_ACCOUNT_INFOS + 1;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    uint8_t data[] = {};
+    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
+                                        arguments, SOL_ARRAY_SIZE(arguments),
+                                        data, SOL_ARRAY_SIZE(data)};
+    const SolSignerSeeds signers_seeds[] = {};
+    sol_assert(SUCCESS == sol_invoke_signed(
+                              &instruction, account_infos, account_infos_len,
+                              signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    break;
+  }
+  case TEST_MAX_ACCOUNT_INFOS_OK: {
+    sol_log("Test max account infos ok");
+    SolAccountMeta arguments[] = {};
+    uint64_t account_infos_len = 255;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    for (uint64_t i = 0; i < account_infos_len; i++) {
+      account_infos[i] = accounts[0];
+    }
+    uint8_t data[] = {};
+    const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
+                                        arguments, SOL_ARRAY_SIZE(arguments),
+                                        data, SOL_ARRAY_SIZE(data)};
+    const SolSignerSeeds signers_seeds[] = {};
+    sol_assert(SUCCESS == sol_invoke_signed(
+                              &instruction, account_infos, account_infos_len,
+                              signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    break;
+  }
   case TEST_MAX_ACCOUNT_INFOS_EXCEEDED: {
     sol_log("Test max account infos exceeded");
     SolAccountMeta arguments[] = {};
-    uint64_t account_infos_len = MAX_CPI_ACCOUNT_INFOS + 1;
+    uint64_t account_infos_len = 256;
     SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
     sol_assert(0 != account_infos);
     uint8_t data[] = {};
@@ -793,7 +897,114 @@ extern uint64_t entrypoint(const uint8_t *input) {
     sol_invoke(&instruction, account_info_acc, params.ka_num);
     break;
   }
+  case TEST_CU_USAGE_MINIMUM:
+  {
+    sol_log("Test minimum cost of a CPI invocation with 1 account meta and 1 account info");
+
+    uint64_t accounts_len = 1;
+    SolAccountMeta arguments[] = {
+      {accounts[NOOP_PROGRAM_INDEX].key, false, false},
+    };
+
+    uint64_t account_infos_len = 1;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    account_infos[0] = accounts[NOOP_PROGRAM_INDEX];
+
+    uint8_t data[] = {};
+    const SolInstruction instruction = {
+      accounts[NOOP_PROGRAM_INDEX].key,
+      arguments, accounts_len,
+      data, SOL_ARRAY_SIZE(data)
+    };
+
+    const SolSignerSeeds signers_seeds[] = {};
+    uint64_t remaining = sol_remaining_compute_units();
+    sol_assert(SUCCESS == sol_invoke_signed(
+                          &instruction,
+                          account_infos, account_infos_len,
+                          signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+
+    uint64_t used = remaining - sol_remaining_compute_units();
+
+    sol_assert(used == 1066);
+    break;
+  }
+  case TEST_CU_USAGE_BASELINE:
+  {
+    sol_log("Test CPI with 255 account metas and 64 account infos");
+
+    uint64_t accounts_len = 255;
+    SolAccountMeta *arguments = sol_calloc(accounts_len, sizeof(SolAccountMeta));
+    sol_assert(0 != arguments);
+
+    for (uint64_t i = 0; i < accounts_len; i++) {
+      arguments[i] = (SolAccountMeta){ accounts[NOOP_PROGRAM_INDEX].key, false, false };
+    }
+
+    uint64_t account_infos_len = 64;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    for (uint64_t i = 0; i < account_infos_len; i++) {
+      account_infos[i] = accounts[NOOP_PROGRAM_INDEX];
+    }
+
+    uint8_t data[] = {};
+    const SolInstruction instruction = {
+      accounts[NOOP_PROGRAM_INDEX].key,
+      arguments, accounts_len,
+      data, SOL_ARRAY_SIZE(data)
+    };
+    const SolSignerSeeds signers_seeds[] = {};
+    uint64_t before = sol_remaining_compute_units();
+    sol_assert(SUCCESS == sol_invoke_signed(
+                          &instruction,
+                          account_infos, account_infos_len,
+                          signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+    uint64_t used = before - sol_remaining_compute_units();
+
+    sol_assert(used == 1120);
+    break;
+  }
+    case TEST_CU_USAGE_MAX:
+  {
+    sol_log("Test CPI with 255 account metas and 255 account infos");
+
+    uint64_t accounts_len = 255;
+    SolAccountMeta *arguments = sol_calloc(accounts_len, sizeof(SolAccountMeta));
+    sol_assert(0 != arguments);
+
+    for (uint64_t i = 0; i < accounts_len; i++) {
+      arguments[i] = (SolAccountMeta){ accounts[NOOP_PROGRAM_INDEX].key, false, false };
+    }
+
+    uint64_t account_infos_len = 255;
+    SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo));
+    sol_assert(0 != account_infos);
+    for (uint64_t i = 0; i < account_infos_len; i++) {
+      account_infos[i] = accounts[NOOP_PROGRAM_INDEX];
+    }
+
+    uint8_t data[] = {};
+
+    const SolInstruction instruction = {
+      accounts[NOOP_PROGRAM_INDEX].key,
+      arguments, accounts_len,
+      data, SOL_ARRAY_SIZE(data)
+    };
+
+    const SolSignerSeeds signers_seeds[] = {};
 
+    uint64_t before = sol_remaining_compute_units();
+    sol_assert(SUCCESS == sol_invoke_signed(
+                          &instruction,
+                          account_infos, account_infos_len,
+                          signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
+    uint64_t used = before - sol_remaining_compute_units();
+    //previous test of 1120 + ((225-64)*80)/250 = 1181 rounded down, which is the extra CU usage for the extra account infos
+    sol_assert(used == 1181);
+    break;
+  }
   default:
     sol_panic();
   }

+ 126 - 1
programs/sbf/rust/invoke/src/lib.rs

@@ -10,6 +10,7 @@ use {
     solana_instruction::Instruction,
     solana_msg::msg,
     solana_program::{
+        compute_units::sol_remaining_compute_units,
         program::{get_return_data, invoke, invoke_signed, set_return_data},
         syscalls::{
             MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
@@ -587,10 +588,45 @@ fn process_instruction<'a>(
                 create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &account_metas, vec![]);
             invoke_signed(&instruction, &[], &[])?;
         }
+        TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339 => {
+            msg!("Test max account infos ok before SIMD-0339 and before increase cpi info");
+            let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
+            let account_infos_len = 64;
+            let account_infos = vec![accounts[0].clone(); account_infos_len];
+            invoke_signed(&instruction, &account_infos, &[])?;
+        }
+        TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339 => {
+            msg!("Test max account infos exceeded before SIMD-0339 and before increase cpi info");
+            let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
+            let account_infos_len = 65;
+            let account_infos = vec![accounts[0].clone(); account_infos_len];
+            invoke_signed(&instruction, &account_infos, &[])?;
+        }
+        TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_SIMD_0339 => {
+            msg!("Test max account infos ok before SIMD-0339");
+            let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
+            let account_infos_len = MAX_CPI_ACCOUNT_INFOS;
+            let account_infos = vec![accounts[0].clone(); account_infos_len];
+            invoke_signed(&instruction, &account_infos, &[])?;
+        }
+        TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_SIMD_0339 => {
+            msg!("Test max account infos exceeded before SIMD-0339");
+            let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
+            let account_infos_len = MAX_CPI_ACCOUNT_INFOS.saturating_add(1);
+            let account_infos = vec![accounts[0].clone(); account_infos_len];
+            invoke_signed(&instruction, &account_infos, &[])?;
+        }
+        TEST_MAX_ACCOUNT_INFOS_OK => {
+            msg!("Test max account infos ok");
+            let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
+            let account_infos_len = 255;
+            let account_infos = vec![accounts[0].clone(); account_infos_len];
+            invoke_signed(&instruction, &account_infos, &[])?;
+        }
         TEST_MAX_ACCOUNT_INFOS_EXCEEDED => {
             msg!("Test max account infos exceeded");
             let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]);
-            let account_infos_len = MAX_CPI_ACCOUNT_INFOS.saturating_add(1);
+            let account_infos_len = 256;
             let account_infos = vec![accounts[0].clone(); account_infos_len];
             invoke_signed(&instruction, &account_infos, &[])?;
         }
@@ -1510,6 +1546,95 @@ fn process_instruction<'a>(
             )
             .unwrap();
         }
+        TEST_CU_USAGE_MINIMUM => {
+            msg!("Test minimum cost of a CPI invocation with 1 account meta and 1 account info");
+
+            let account_infos: Vec<AccountInfo<'_>> = vec![accounts[NOOP_PROGRAM_INDEX].clone()];
+
+            let account_metas: Vec<(&Pubkey, bool, bool)> =
+                vec![(accounts[NOOP_PROGRAM_INDEX].key, false, false)];
+
+            let instruction =
+                create_instruction(*accounts[NOOP_PROGRAM_INDEX].key, &account_metas, vec![]);
+
+            let before_cpi = sol_remaining_compute_units();
+            invoke_signed(&instruction, &account_infos, &[])?;
+            let after_cpi = sol_remaining_compute_units();
+            let cu_used = before_cpi - after_cpi;
+            //need to use upper bound here, as different versions of sbpf add/remove speciliazed intructions hence leading to different CU usage
+            if cu_used > 1756 {
+                panic!("CU used more than baseline");
+            }
+        }
+        TEST_CU_USAGE_BASELINE => {
+            msg!(
+                "Test minimum cost of a CPI invocation with up to 255 account metas and 64 \
+                 account infos"
+            );
+
+            let mut selected_indices: Vec<usize> = vec![NOOP_PROGRAM_INDEX];
+
+            while selected_indices.len() < 64 {
+                selected_indices.push(selected_indices[0]);
+            }
+
+            let account_infos: Vec<AccountInfo<'_>> = selected_indices
+                .iter()
+                .map(|&i| accounts[i].clone())
+                .collect();
+
+            let mut account_metas: Vec<(&Pubkey, bool, bool)> = Vec::with_capacity(255);
+            account_metas.push((accounts[NOOP_PROGRAM_INDEX].key, false, false));
+
+            while account_metas.len() < 255 {
+                account_metas.push((accounts[NOOP_PROGRAM_INDEX].key, false, false));
+            }
+
+            let instruction =
+                create_instruction(*accounts[NOOP_PROGRAM_INDEX].key, &account_metas, vec![]);
+
+            let before_cpi = sol_remaining_compute_units();
+            invoke_signed(&instruction, &account_infos, &[])?;
+            let after_cpi = sol_remaining_compute_units();
+            let cu_used = before_cpi - after_cpi;
+            if cu_used > 48212 {
+                panic!("CU used more than baseline");
+            }
+        }
+        TEST_CU_USAGE_MAX => {
+            msg!(
+                "Test minimum cost of a CPI invocation with up to 255 account metas and 255 \
+                 account infos"
+            );
+            //this is currently using 64 account infos due to heap memmory running out
+            let mut selected_indices: Vec<usize> = vec![NOOP_PROGRAM_INDEX];
+            while selected_indices.len() < 64 {
+                selected_indices.push(selected_indices[0]);
+            }
+
+            let account_infos: Vec<AccountInfo<'_>> = selected_indices
+                .iter()
+                .map(|&i| accounts[i].clone())
+                .collect();
+
+            let mut account_metas: Vec<(&Pubkey, bool, bool)> = Vec::with_capacity(255);
+
+            while account_metas.len() < 255 {
+                account_metas.push((accounts[NOOP_PROGRAM_INDEX].key, false, false));
+            }
+
+            let instruction =
+                create_instruction(*accounts[NOOP_PROGRAM_INDEX].key, &account_metas, vec![]);
+
+            let before_cpi = sol_remaining_compute_units();
+            invoke_signed(&instruction, &account_infos, &[])?;
+            let after_cpi = sol_remaining_compute_units();
+            let cu_used = before_cpi - after_cpi;
+            // previous test + 61
+            if cu_used > 48212 {
+                panic!("CU usedmore than baseline");
+            }
+        }
         _ => panic!("unexpected program data"),
     }
 

+ 10 - 1
programs/sbf/rust/invoke_dep/src/lib.rs

@@ -21,7 +21,7 @@ pub const ADD_LAMPORTS: u8 = 18;
 pub const TEST_RETURN_DATA_TOO_LARGE: u8 = 19;
 pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER: u8 = 20;
 pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE: u8 = 21;
-pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED: u8 = 22;
+pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_SIMD_0339: u8 = 22;
 pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE: u8 = 23;
 pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED: u8 = 24;
 pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER: u8 = 25;
@@ -47,6 +47,14 @@ pub const TEST_ACCOUNT_INFO_LAMPORTS_RC: u8 = 44;
 pub const TEST_ACCOUNT_INFO_DATA_RC: u8 = 45;
 pub const TEST_NESTED_INVOKE_SIMD_0268_OK: u8 = 46;
 pub const TEST_NESTED_INVOKE_SIMD_0268_TOO_DEEP: u8 = 47;
+pub const TEST_MAX_ACCOUNT_INFOS_OK: u8 = 48;
+pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED: u8 = 49;
+pub const TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_SIMD_0339: u8 = 50;
+pub const TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339: u8 = 51;
+pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339: u8 = 52;
+pub const TEST_CU_USAGE_MINIMUM: u8 = 53;
+pub const TEST_CU_USAGE_BASELINE: u8 = 54;
+pub const TEST_CU_USAGE_MAX: u8 = 55;
 
 pub const MINT_INDEX: usize = 0;
 pub const ARGUMENT_INDEX: usize = 1;
@@ -62,3 +70,4 @@ pub const FROM_INDEX: usize = 10;
 pub const ED25519_PROGRAM_INDEX: usize = 11;
 pub const INVOKE_PROGRAM_INDEX: usize = 12;
 pub const UNEXECUTABLE_PROGRAM_INDEX: usize = 13;
+pub const NOOP_PROGRAM_INDEX: usize = 14;

+ 137 - 0
programs/sbf/tests/programs.rs

@@ -692,6 +692,10 @@ fn test_program_sbf_invoke_sanity() {
         let account = AccountSharedData::new(1, 0, &bpf_loader::id());
         bank.store_account(&unexecutable_program_keypair.pubkey(), &account);
 
+        let noop_program_keypair = Keypair::new();
+        let account = AccountSharedData::new(42, 5, &noop_program_id);
+        bank.store_account(&noop_program_keypair.pubkey(), &account);
+
         let (derived_key1, bump_seed1) =
             Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id);
         let (derived_key2, bump_seed2) =
@@ -715,6 +719,7 @@ fn test_program_sbf_invoke_sanity() {
             AccountMeta::new_readonly(solana_sdk_ids::ed25519_program::id(), false),
             AccountMeta::new_readonly(invoke_program_id, false),
             AccountMeta::new_readonly(unexecutable_program_keypair.pubkey(), false),
+            AccountMeta::new_readonly(noop_program_id, false),
         ];
 
         let do_invoke = |test: u8, additional_instructions: &[Instruction], bank: &Bank| {
@@ -871,7 +876,80 @@ fn test_program_sbf_invoke_sanity() {
             &[invoked_program_id.clone(); 16], // 16, 8 for each invoke
             &bank,
         );
+        do_invoke_success(
+            TEST_MAX_ACCOUNT_INFOS_OK,
+            &[],
+            &[invoked_program_id.clone()],
+            &bank,
+        );
 
+        do_invoke_success(
+            TEST_CU_USAGE_MINIMUM,
+            &[],
+            &[noop_program_id.clone()],
+            &bank,
+        );
+
+        do_invoke_success(
+            TEST_CU_USAGE_BASELINE,
+            &[],
+            &[noop_program_id.clone()],
+            &bank,
+        );
+
+        do_invoke_success(TEST_CU_USAGE_MAX, &[], &[noop_program_id.clone()], &bank);
+
+        let bank = bank_with_feature_deactivated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_cpi_account_info_limit::id(),
+        );
+
+        assert!(!bank
+            .feature_set
+            .is_active(&feature_set::increase_cpi_account_info_limit::id()));
+
+        do_invoke_success(
+            TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_SIMD_0339,
+            &[],
+            &[invoked_program_id.clone()],
+            &bank,
+        );
+
+        let bank = bank_with_feature_deactivated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_tx_account_lock_limit::id(),
+        );
+        assert!(!bank
+            .feature_set
+            .is_active(&feature_set::increase_tx_account_lock_limit::id()));
+
+        do_invoke_success(
+            TEST_MAX_ACCOUNT_INFOS_OK_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339,
+            &[],
+            &[invoked_program_id.clone()],
+            &bank,
+        );
+        let bank = bank_with_feature_activated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_tx_account_lock_limit::id(),
+        );
+
+        assert!(bank
+            .feature_set
+            .is_active(&feature_set::increase_tx_account_lock_limit::id()));
+
+        let bank = bank_with_feature_activated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_cpi_account_info_limit::id(),
+        );
+
+        assert!(bank
+            .feature_set
+            .is_active(&feature_set::increase_cpi_account_info_limit::id()));
         // failure cases
 
         let do_invoke_failure_test_local_with_compute_check =
@@ -1032,6 +1110,33 @@ fn test_program_sbf_invoke_sanity() {
                 format!("Program log: invoke {program_lang} program"),
                 "Program log: Test max account infos exceeded".into(),
                 "skip".into(), // don't compare compute consumption logs
+                format!(
+                    "Program {invoke_program_id} failed: Invoked an instruction with too many \
+                     account info's (256 > 255)"
+                ),
+            ]),
+            &bank,
+        );
+
+        let bank = bank_with_feature_deactivated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_cpi_account_info_limit::id(),
+        );
+
+        assert!(!bank
+            .feature_set
+            .is_active(&feature_set::increase_cpi_account_info_limit::id()));
+
+        do_invoke_failure_test_local(
+            TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_SIMD_0339,
+            TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
+            &[],
+            Some(vec![
+                format!("Program {invoke_program_id} invoke [1]"),
+                format!("Program log: invoke {program_lang} program"),
+                "Program log: Test max account infos exceeded before SIMD-0339".into(),
+                "skip".into(), // don't compare compute consumption logs
                 format!(
                     "Program {invoke_program_id} failed: Invoked an instruction with too many \
                      account info's (129 > 128)"
@@ -1040,6 +1145,35 @@ fn test_program_sbf_invoke_sanity() {
             &bank,
         );
 
+        let bank = bank_with_feature_deactivated(
+            &bank_forks,
+            bank,
+            &feature_set::increase_tx_account_lock_limit::id(),
+        );
+
+        assert!(!bank
+            .feature_set
+            .is_active(&feature_set::increase_tx_account_lock_limit::id()));
+
+        do_invoke_failure_test_local(
+            TEST_MAX_ACCOUNT_INFOS_EXCEEDED_BEFORE_INCREASE_TX_ACCOUNT_LOCK_BEFORE_SIMD_0339,
+            TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
+            &[],
+            Some(vec![
+                format!("Program {invoke_program_id} invoke [1]"),
+                format!("Program log: invoke {program_lang} program"),
+                "Program log: Test max account infos exceeded before SIMD-0339 and before \
+                 increase cpi info"
+                    .into(),
+                "skip".into(), // don't compare compute consumption logs
+                format!(
+                    "Program {invoke_program_id} failed: Invoked an instruction with too many \
+                     account info's (65 > 64)"
+                ),
+            ]),
+            &bank,
+        );
+
         do_invoke_failure_test_local(
             TEST_RETURN_ERROR,
             TransactionError::InstructionError(0, InstructionError::Custom(42)),
@@ -1378,6 +1512,9 @@ fn test_program_sbf_call_depth() {
         genesis_config
             .accounts
             .contains_key(&feature_set::raise_cpi_nesting_limit_to_8::id()),
+        genesis_config
+            .accounts
+            .contains_key(&feature_set::increase_cpi_account_info_limit::id()),
     );
     let instruction =
         Instruction::new_with_bincode(program_id, &(budget.max_call_depth - 1), vec![]);

+ 39 - 2
runtime/src/bank.rs

@@ -56,7 +56,10 @@ use {
         transaction_batch::{OwnedOrBorrowed, TransactionBatch},
     },
     accounts_lt_hash::{CacheValue as AccountsLtHashCacheValue, Stats as AccountsLtHashStats},
-    agave_feature_set::{self as feature_set, raise_cpi_nesting_limit_to_8, FeatureSet},
+    agave_feature_set::{
+        self as feature_set, increase_cpi_account_info_limit, raise_cpi_nesting_limit_to_8,
+        FeatureSet,
+    },
     agave_precompiles::{get_precompile, get_precompiles, is_precompile},
     agave_reserved_account_keys::ReservedAccountKeys,
     agave_snapshots::snapshot_hash::SnapshotHash,
@@ -4056,6 +4059,26 @@ impl Bank {
         cost_tracker.set_limits(account_cost_limit, block_cost_limit, vote_cost_limit);
     }
 
+    fn apply_simd_0339_invoke_cost_changes(&mut self) {
+        let simd_0268_active = self
+            .feature_set
+            .is_active(&raise_cpi_nesting_limit_to_8::id());
+        let simd_0339_active = self
+            .feature_set
+            .is_active(&increase_cpi_account_info_limit::id());
+        let compute_budget = self
+            .compute_budget()
+            .as_ref()
+            .unwrap_or(&ComputeBudget::new_with_defaults(
+                simd_0268_active,
+                simd_0339_active,
+            ))
+            .to_cost();
+
+        self.transaction_processor
+            .set_execution_cost(compute_budget);
+    }
+
     /// This is called from genesis and snapshot restore
     fn apply_activated_features(&mut self) {
         // Update active set of reserved account keys which are not allowed to be write locked
@@ -4090,6 +4113,13 @@ impl Bank {
             self.apply_simd_0306_cost_tracker_changes();
         }
 
+        if self
+            .feature_set
+            .is_active(&feature_set::increase_cpi_account_info_limit::id())
+        {
+            self.apply_simd_0339_invoke_cost_changes();
+        }
+
         let (program_runtime_environment_v1, program_runtime_environment_v2) =
             self.create_program_runtime_environments(&self.feature_set);
         self.transaction_processor
@@ -4104,10 +4134,14 @@ impl Bank {
         feature_set: &FeatureSet,
     ) -> (ProgramRuntimeEnvironment, ProgramRuntimeEnvironment) {
         let simd_0268_active = feature_set.is_active(&raise_cpi_nesting_limit_to_8::id());
+        let simd_0339_active = feature_set.is_active(&increase_cpi_account_info_limit::id());
         let compute_budget = self
             .compute_budget()
             .as_ref()
-            .unwrap_or(&ComputeBudget::new_with_defaults(simd_0268_active))
+            .unwrap_or(&ComputeBudget::new_with_defaults(
+                simd_0268_active,
+                simd_0339_active,
+            ))
             .to_budget();
         (
             Arc::new(
@@ -5316,6 +5350,9 @@ impl Bank {
                 error!("Failed to upgrade Core BPF Stake program: {e}");
             }
         }
+        if new_feature_activations.contains(&feature_set::increase_cpi_account_info_limit::id()) {
+            self.apply_simd_0339_invoke_cost_changes();
+        }
     }
 
     fn apply_new_builtin_program_feature_transitions(

+ 1 - 1
runtime/src/bank/builtins/core_bpf_migration/mod.rs

@@ -146,7 +146,7 @@ impl Bank {
             let compute_budget = self
                 .compute_budget()
                 .unwrap_or(ComputeBudget::new_with_defaults(
-                    /* simd_0268_active */ false,
+                    /* simd_0268_active */ false, /* simd_0339_active */ false,
                 ));
             let mut sysvar_cache = SysvarCache::default();
             sysvar_cache.fill_missing_entries(|pubkey, set_sysvar| {

+ 12 - 3
runtime/src/bank/tests.rs

@@ -8845,7 +8845,10 @@ fn test_compute_budget_program_noop() {
                 ..ComputeBudget::new_with_defaults(
                     invoke_context
                         .get_feature_set()
-                        .raise_cpi_nesting_limit_to_8
+                        .raise_cpi_nesting_limit_to_8,
+                    invoke_context
+                        .get_feature_set()
+                        .increase_cpi_account_info_limit
                 )
             }
         );
@@ -8897,7 +8900,10 @@ fn test_compute_request_instruction() {
                 ..ComputeBudget::new_with_defaults(
                     invoke_context
                         .get_feature_set()
-                        .raise_cpi_nesting_limit_to_8
+                        .raise_cpi_nesting_limit_to_8,
+                    invoke_context
+                        .get_feature_set()
+                        .increase_cpi_account_info_limit
                 )
             }
         );
@@ -8956,7 +8962,10 @@ fn test_failed_compute_request_instruction() {
                 ..ComputeBudget::new_with_defaults(
                     invoke_context
                         .get_feature_set()
-                        .raise_cpi_nesting_limit_to_8
+                        .raise_cpi_nesting_limit_to_8,
+                    invoke_context
+                        .get_feature_set()
+                        .increase_cpi_account_info_limit
                 )
             }
         );

+ 2 - 0
svm-feature-set/src/lib.rs

@@ -47,6 +47,7 @@ pub struct SVMFeatureSet {
     pub reenable_zk_elgamal_proof_program: bool,
     pub raise_cpi_nesting_limit_to_8: bool,
     pub provide_instruction_data_offset_in_vm_r2: bool,
+    pub increase_cpi_account_info_limit: bool,
     pub vote_state_v4: bool,
 }
 
@@ -91,6 +92,7 @@ impl SVMFeatureSet {
             reenable_zk_elgamal_proof_program: true,
             raise_cpi_nesting_limit_to_8: true,
             provide_instruction_data_offset_in_vm_r2: true,
+            increase_cpi_account_info_limit: true,
             vote_state_v4: true,
         }
     }

+ 1 - 1
svm-test-harness/src/instr.rs

@@ -190,7 +190,7 @@ pub fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
 
     let log_collector = LogCollector::new_ref();
     let compute_budget = {
-        let mut budget = ComputeBudget::new_with_defaults(false);
+        let mut budget = ComputeBudget::new_with_defaults(false, false);
         budget.compute_unit_limit = input.cu_avail;
         budget
     };

+ 7 - 1
test-validator/src/lib.rs

@@ -1,6 +1,9 @@
 #![allow(clippy::arithmetic_side_effects)]
 use {
-    agave_feature_set::{alpenglow, raise_cpi_nesting_limit_to_8, FeatureSet, FEATURE_NAMES},
+    agave_feature_set::{
+        alpenglow, increase_cpi_account_info_limit, raise_cpi_nesting_limit_to_8, FeatureSet,
+        FEATURE_NAMES,
+    },
     agave_snapshots::{
         paths::BANK_SNAPSHOTS_DIR, snapshot_config::SnapshotConfig, SnapshotInterval,
     },
@@ -1086,6 +1089,9 @@ impl TestValidator {
                         !config
                             .deactivate_feature_set
                             .contains(&raise_cpi_nesting_limit_to_8::id()),
+                        !config
+                            .deactivate_feature_set
+                            .contains(&increase_cpi_account_info_limit::id()),
                     )
                 }),
             log_messages_bytes_limit: config.log_messages_bytes_limit,