Runtime feature activation framework (bp #12376) (#12497)

* Runtime feature activation framework

(cherry picked from commit 93259f0bae)

# Conflicts:
#	runtime/src/bank.rs

* Add feature set identifier to gossiped version information

(cherry picked from commit 35f5f9fc7b)

# Conflicts:
#	Cargo.lock
#	version/Cargo.toml

* Port instructions sysvar and secp256k1 program activation to FeatureSet

(cherry picked from commit c10da16d7b)

# Conflicts:
#	runtime/src/bank.rs
#	runtime/src/message_processor.rs

* Add feature management commands

(cherry picked from commit 93ed0ab2bb)

# Conflicts:
#	Cargo.lock
#	cli/Cargo.toml

* Make test_process_rest_api less fragile

(cherry picked from commit 7526bb96f3)

* Remove id field

(cherry picked from commit cc6ba1e131)

* FeatureSet test

(cherry picked from commit 92406cf9a0)

* cargo fmt

(cherry picked from commit 199940d683)

* cli review feedback

(cherry picked from commit 3a2b8c5e5b)

* Rename active() to is_active()

(cherry picked from commit e39fac9f01)

* Resolve merge conflicts

* Remove continues from compute_active_feature_set()

Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
mergify[bot]
2020-09-26 17:49:53 +00:00
committed by GitHub
parent 6a698af235
commit bc3aa53e02
32 changed files with 808 additions and 233 deletions

View File

@@ -6,6 +6,7 @@ use crate::{
append_vec::StoredAccount,
bank::{HashAgeKind, TransactionProcessResult},
blockhash_queue::BlockhashQueue,
feature_set::{self, FeatureSet},
nonce_utils,
rent_collector::RentCollector,
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
@@ -17,7 +18,7 @@ use rayon::slice::ParallelSliceMut;
use solana_sdk::{
account::Account,
clock::{Epoch, Slot},
fee_calculator::FeeCalculator,
fee_calculator::{FeeCalculator, FeeConfig},
genesis_config::ClusterType,
hash::Hash,
message::Message,
@@ -72,11 +73,10 @@ pub enum AccountAddressFilter {
impl Accounts {
pub fn new(paths: Vec<PathBuf>, cluster_type: &ClusterType) -> Self {
Self {
slot: 0,
epoch: 0,
accounts_db: Arc::new(AccountsDB::new(paths, cluster_type)),
account_locks: Mutex::new(HashSet::new()),
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
..Self::default()
}
}
@@ -94,11 +94,10 @@ impl Accounts {
pub(crate) fn new_empty(accounts_db: AccountsDB) -> Self {
Self {
slot: 0,
epoch: 0,
accounts_db: Arc::new(accounts_db),
account_locks: Mutex::new(HashSet::new()),
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
..Self::default()
}
}
@@ -133,6 +132,7 @@ impl Accounts {
fee: u64,
error_counters: &mut ErrorCounters,
rent_collector: &RentCollector,
feature_set: &FeatureSet,
) -> Result<(TransactionAccounts, TransactionRent)> {
// Copy all the accounts
let message = tx.message();
@@ -150,10 +150,8 @@ impl Accounts {
payer_index = Some(i);
}
if solana_sdk::sysvar::instructions::is_enabled(
self.epoch,
self.accounts_db.cluster_type.unwrap(),
) && solana_sdk::sysvar::instructions::check_id(key)
if solana_sdk::sysvar::instructions::check_id(key)
&& feature_set.is_active(&feature_set::instructions_sysvar_enabled::id())
{
if message.is_writable(i) {
return Err(TransactionError::InvalidAccountIndex);
@@ -300,11 +298,17 @@ impl Accounts {
hash_queue: &BlockhashQueue,
error_counters: &mut ErrorCounters,
rent_collector: &RentCollector,
feature_set: &FeatureSet,
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
//PERF: hold the lock to scan for the references, but not to clone the accounts
//TODO: two locks usually leads to deadlocks, should this be one structure?
let accounts_index = self.accounts_db.accounts_index.read().unwrap();
let storage = self.accounts_db.storage.read().unwrap();
let fee_config = FeeConfig {
secp256k1_program_enabled: feature_set
.is_active(&feature_set::secp256k1_program_enabled::id()),
};
OrderedIterator::new(txs, txs_iteration_order)
.zip(lock_results.into_iter())
.map(|etx| match etx {
@@ -318,13 +322,7 @@ impl Accounts {
.cloned(),
};
let fee = if let Some(fee_calculator) = fee_calculator {
fee_calculator.calculate_fee(
tx.message(),
solana_sdk::secp256k1::get_fee_config(
self.accounts_db.cluster_type.unwrap(),
self.epoch,
),
)
fee_calculator.calculate_fee_with_config(tx.message(), &fee_config)
} else {
return (Err(TransactionError::BlockhashNotFound), hash_age_kind);
};
@@ -337,6 +335,7 @@ impl Accounts {
fee,
error_counters,
rent_collector,
feature_set,
);
let (accounts, rents) = match load_res {
Ok((a, r)) => (a, r),
@@ -888,6 +887,7 @@ mod tests {
&hash_queue,
error_counters,
rent_collector,
&FeatureSet::default(),
)
}
@@ -1024,7 +1024,7 @@ mod tests {
);
let fee_calculator = FeeCalculator::new(10);
assert_eq!(fee_calculator.calculate_fee(tx.message(), None), 10);
assert_eq!(fee_calculator.calculate_fee(tx.message()), 10);
let loaded_accounts =
load_accounts_with_fee(tx, &accounts, &fee_calculator, &mut error_counters);
@@ -1832,6 +1832,7 @@ mod tests {
&hash_queue,
&mut error_counters,
&rent_collector,
&FeatureSet::default(),
)
}

View File

@@ -10,8 +10,10 @@ use crate::{
accounts_db::{ErrorCounters, SnapshotStorages},
accounts_index::Ancestors,
blockhash_queue::BlockhashQueue,
builtins::get_builtins,
builtins::*,
epoch_stakes::{EpochStakes, NodeVoteAccounts},
feature::Feature,
feature_set::{self, FeatureSet},
instruction_recorder::InstructionRecorder,
log_collector::LogCollector,
message_processor::{Executors, MessageProcessor},
@@ -41,7 +43,7 @@ use solana_sdk::{
},
epoch_info::EpochInfo,
epoch_schedule::EpochSchedule,
fee_calculator::{FeeCalculator, FeeRateGovernor},
fee_calculator::{FeeCalculator, FeeConfig, FeeRateGovernor},
genesis_config::{ClusterType, GenesisConfig},
hard_forks::HardForks,
hash::{extend_and_hash, hashv, Hash},
@@ -526,6 +528,8 @@ pub struct Bank {
cached_executors: Arc<RwLock<CachedExecutors>>,
transaction_debug_keys: Option<Arc<HashSet<Pubkey>>>,
pub feature_set: Arc<FeatureSet>,
}
impl Default for BlockhashQueue {
@@ -651,6 +655,7 @@ impl Bank {
rewards_pool_pubkeys: parent.rewards_pool_pubkeys.clone(),
cached_executors: parent.cached_executors.clone(),
transaction_debug_keys: parent.transaction_debug_keys.clone(),
feature_set: parent.feature_set.clone(),
};
datapoint_info!(
@@ -756,6 +761,7 @@ impl Bank {
rewards_pool_pubkeys: new(),
cached_executors: Arc::new(RwLock::new(CachedExecutors::new(MAX_CACHED_EXECUTORS))),
transaction_debug_keys: debug_keys,
feature_set: new(),
};
bank.finish_init(genesis_config);
@@ -1607,6 +1613,7 @@ impl Bank {
&self.blockhash_queue.read().unwrap(),
error_counters,
&self.rent_collector,
&self.feature_set,
)
}
fn check_age(
@@ -2045,8 +2052,7 @@ impl Bank {
log_collector.clone(),
executors.clone(),
instruction_recorders.as_deref(),
self.cluster_type(),
self.epoch(),
&self.feature_set,
);
Self::compile_recorded_instructions(
@@ -2127,6 +2133,11 @@ impl Bank {
) -> Vec<Result<()>> {
let hash_queue = self.blockhash_queue.read().unwrap();
let mut fees = 0;
let fee_config = FeeConfig {
secp256k1_program_enabled: self.secp256k1_program_enabled(),
};
let results = OrderedIterator::new(txs, iteration_order)
.zip(executed.iter())
.map(|((_, tx), (res, hash_age_kind))| {
@@ -2143,10 +2154,7 @@ impl Bank {
};
let fee_calculator = fee_calculator.ok_or(TransactionError::BlockhashNotFound)?;
let fee = fee_calculator.calculate_fee(
tx.message(),
solana_sdk::secp256k1::get_fee_config(self.cluster_type(), self.epoch()),
);
let fee = fee_calculator.calculate_fee_with_config(tx.message(), &fee_config);
let message = tx.message();
match *res {
@@ -3486,10 +3494,16 @@ impl Bank {
consumed_budget.saturating_sub(budget_recovery_delta)
}
pub fn secp256k1_program_enabled(&self) -> bool {
self.feature_set
.is_active(&feature_set::secp256k1_program_enabled::id())
}
// This is called from snapshot restore AND for each epoch boundary
// The entire code path herein must be idempotent
fn apply_feature_activations(&mut self, init_finish_or_warp: bool, initiate_callback: bool) {
self.ensure_builtins(init_finish_or_warp);
let new_feature_activations = self.compute_active_feature_set(!init_finish_or_warp);
self.ensure_builtins(init_finish_or_warp, &new_feature_activations);
self.reinvoke_entered_epoch_callback(initiate_callback);
self.recheck_cross_program_support();
self.recheck_compute_budget();
@@ -3497,14 +3511,66 @@ impl Bank {
self.ensure_no_storage_rewards_pool();
}
fn ensure_builtins(&mut self, init_or_warp: bool) {
for (program, start_epoch) in get_builtins(self.cluster_type()) {
// Compute the active feature set based on the current bank state, and return the set of newly activated features
fn compute_active_feature_set(&mut self, allow_new_activations: bool) -> HashSet<Pubkey> {
let mut active = self.feature_set.active.clone();
let mut inactive = HashSet::new();
let mut newly_activated = HashSet::new();
let slot = self.slot();
for feature_id in &self.feature_set.inactive {
let mut activated = false;
if let Some(mut account) = self.get_account(feature_id) {
if let Some(mut feature) = Feature::from_account(&account) {
match feature.activated_at {
None => {
if allow_new_activations {
// Feature has been requested, activate it now
feature.activated_at = Some(slot);
if feature.to_account(&mut account).is_some() {
self.store_account(feature_id, &account);
}
newly_activated.insert(*feature_id);
activated = true;
info!("Feature {} activated at slot {}", feature_id, slot);
}
}
Some(activation_slot) => {
if slot >= activation_slot {
// Feature is already active
activated = true;
}
}
}
}
}
if activated {
active.insert(*feature_id);
} else {
inactive.insert(*feature_id);
}
}
self.feature_set = Arc::new(FeatureSet { active, inactive });
newly_activated
}
fn ensure_builtins(&mut self, init_or_warp: bool, new_feature_activations: &HashSet<Pubkey>) {
for (program, start_epoch) in get_cluster_builtins(self.cluster_type()) {
let should_populate = init_or_warp && self.epoch() >= start_epoch
|| !init_or_warp && self.epoch() == start_epoch;
if should_populate {
self.add_builtin(&program.name, program.id, program.entrypoint);
}
}
for (program, feature) in get_feature_builtins() {
let should_populate = init_or_warp && self.feature_set.is_active(&feature)
|| !init_or_warp && new_feature_activations.contains(&feature);
if should_populate {
self.add_builtin(&program.name, program.id, program.entrypoint);
}
}
}
fn reinvoke_entered_epoch_callback(&mut self, initiate: bool) {
@@ -8508,7 +8574,7 @@ mod tests {
.collect::<Vec<_>>();
consumed_budgets.sort();
// consumed_budgets represents the count of alive accounts in the three slots 0,1,2
assert_eq!(consumed_budgets, vec![0, 1, 10]);
assert_eq!(consumed_budgets, vec![0, 1, 9]);
}
#[test]
@@ -8973,4 +9039,59 @@ mod tests {
assert!(executors.borrow().executors.contains_key(&key3));
assert!(executors.borrow().executors.contains_key(&key4));
}
#[test]
fn test_compute_active_feature_set() {
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
let bank0 = Arc::new(Bank::new(&genesis_config));
let mut bank = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
let test_feature = "TestFeature11111111111111111111111111111111"
.parse::<Pubkey>()
.unwrap();
let mut feature_set = FeatureSet::default();
feature_set.inactive.insert(test_feature);
bank.feature_set = Arc::new(feature_set.clone());
let new_activations = bank.compute_active_feature_set(true);
assert!(new_activations.is_empty());
assert!(!bank.feature_set.is_active(&test_feature));
// Depositing into the `test_feature` account should do nothing
bank.deposit(&test_feature, 42);
let new_activations = bank.compute_active_feature_set(true);
assert!(new_activations.is_empty());
assert!(!bank.feature_set.is_active(&test_feature));
// Request `test_feature` activation
let feature = Feature::default();
assert_eq!(feature.activated_at, None);
bank.store_account(&test_feature, &feature.create_account(42));
// Run `compute_active_feature_set` disallowing new activations
let new_activations = bank.compute_active_feature_set(false);
assert!(new_activations.is_empty());
assert!(!bank.feature_set.is_active(&test_feature));
let feature = Feature::from_account(&bank.get_account(&test_feature).expect("get_account"))
.expect("from_account");
assert_eq!(feature.activated_at, None);
// Run `compute_active_feature_set` allowing new activations
let new_activations = bank.compute_active_feature_set(true);
assert_eq!(new_activations.len(), 1);
assert!(bank.feature_set.is_active(&test_feature));
let feature = Feature::from_account(&bank.get_account(&test_feature).expect("get_account"))
.expect("from_account");
assert_eq!(feature.activated_at, Some(1));
// Reset the bank's feature set
bank.feature_set = Arc::new(feature_set);
assert!(!bank.feature_set.is_active(&test_feature));
// Running `compute_active_feature_set` will not cause new activations, but
// `test_feature` is now be active
let new_activations = bank.compute_active_feature_set(true);
assert!(new_activations.is_empty());
assert!(bank.feature_set.is_active(&test_feature));
}
}

View File

@@ -1,18 +1,21 @@
use crate::{
bank::{Builtin, Entrypoint},
system_instruction_processor,
feature_set, system_instruction_processor,
};
use solana_sdk::{
clock::{Epoch, GENESIS_EPOCH},
genesis_config::ClusterType,
pubkey::Pubkey,
system_program,
};
use log::*;
/// The entire set of available builtin programs that should be active at the given cluster_type
pub fn get_builtins(cluster_type: ClusterType) -> Vec<(Builtin, Epoch)> {
trace!("get_builtins: {:?}", cluster_type);
/// Builtin programs that should be active for the given cluster_type
///
/// Old style. Use `get_feature_builtins()` instead
pub fn get_cluster_builtins(cluster_type: ClusterType) -> Vec<(Builtin, Epoch)> {
trace!("get_cluster_builtins: {:?}", cluster_type);
let mut builtins = vec![];
builtins.extend(
@@ -46,8 +49,8 @@ pub fn get_builtins(cluster_type: ClusterType) -> Vec<(Builtin, Epoch)> {
// repurpose Testnet for test_get_builtins because the Development is overloaded...
#[cfg(test)]
if cluster_type == ClusterType::Testnet {
use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError;
use solana_sdk::{account::KeyedAccount, pubkey::Pubkey};
use std::str::FromStr;
fn mock_ix_processor(
_pubkey: &Pubkey,
@@ -57,35 +60,33 @@ pub fn get_builtins(cluster_type: ClusterType) -> Vec<(Builtin, Epoch)> {
Err(InstructionError::Custom(42))
}
let program_id = Pubkey::from_str("7saCc6X5a2syoYANA5oUUnPZLcLMfKoSjiDhFU5fbpoK").unwrap();
builtins.extend(vec![(
builtins.push((
Builtin::new("mock", program_id, Entrypoint::Program(mock_ix_processor)),
2,
)]);
));
}
let secp256k1_builtin = Builtin::new(
"secp256k1_program",
solana_sdk::secp256k1_program::id(),
Entrypoint::Program(solana_secp256k1_program::process_instruction),
);
let secp_epoch = solana_sdk::secp256k1::is_enabled_epoch(cluster_type);
builtins.push((secp256k1_builtin, secp_epoch));
builtins
}
/// Builtin programs that are activated dynamically by feature
pub fn get_feature_builtins() -> Vec<(Builtin, Pubkey)> {
vec![(
Builtin::new(
"secp256k1_program",
solana_sdk::secp256k1_program::id(),
Entrypoint::Program(solana_secp256k1_program::process_instruction),
),
feature_set::secp256k1_program_enabled::id(),
)]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bank::Bank;
use solana_sdk::{
genesis_config::{create_genesis_config, ClusterType},
pubkey::Pubkey,
};
use std::collections::HashSet;
use std::str::FromStr;
use std::sync::Arc;
use solana_sdk::genesis_config::create_genesis_config;
use std::{collections::HashSet, str::FromStr, sync::Arc};
fn do_test_uniqueness(builtins: Vec<(Builtin, Epoch)>) {
let mut unique_ids = HashSet::new();
@@ -101,10 +102,10 @@ mod tests {
#[test]
fn test_uniqueness() {
do_test_uniqueness(get_builtins(ClusterType::Development));
do_test_uniqueness(get_builtins(ClusterType::Devnet));
do_test_uniqueness(get_builtins(ClusterType::Testnet));
do_test_uniqueness(get_builtins(ClusterType::MainnetBeta));
do_test_uniqueness(get_cluster_builtins(ClusterType::Development));
do_test_uniqueness(get_cluster_builtins(ClusterType::Devnet));
do_test_uniqueness(get_cluster_builtins(ClusterType::Testnet));
do_test_uniqueness(get_cluster_builtins(ClusterType::MainnetBeta));
}
#[test]

42
runtime/src/feature.rs Normal file
View File

@@ -0,0 +1,42 @@
use solana_sdk::{account::Account, clock::Slot};
solana_sdk::declare_id!("Feature111111111111111111111111111111111111");
/// The `Feature` struct is the on-chain representation of a runtime feature.
///
/// Feature activation is accomplished by:
/// 1. Activation is requested by the feature authority, who issues a transaction to create the
/// feature account. The newly created feature account will have the value of
/// `Feature::default()`
/// 2. When the next epoch is entered the runtime will check for new activation requests and
/// active them. When this occurs, the activation slot is recorded in the feature account
///
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Feature {
pub activated_at: Option<Slot>,
}
impl Feature {
pub fn size_of() -> usize {
bincode::serialized_size(&Self {
activated_at: Some(Slot::MAX),
})
.unwrap() as usize
}
pub fn from_account(account: &Account) -> Option<Self> {
if account.owner != id() {
None
} else {
bincode::deserialize(&account.data).ok()
}
}
pub fn to_account(&self, account: &mut Account) -> Option<()> {
bincode::serialize_into(&mut account.data[..], self).ok()
}
pub fn create_account(&self, lamports: u64) -> Account {
let data_len = Self::size_of().max(bincode::serialized_size(self).unwrap() as usize);
let mut account = Account::new(lamports, data_len, &id());
self.to_account(&mut account).unwrap();
account
}
}

View File

@@ -0,0 +1,69 @@
use lazy_static::lazy_static;
use solana_sdk::{
hash::{Hash, Hasher},
pubkey::Pubkey,
};
use std::collections::{HashMap, HashSet};
pub mod instructions_sysvar_enabled {
solana_sdk::declare_id!("EnvhHCLvg55P7PDtbvR1NwuTuAeodqpusV3MR5QEK8gs");
}
pub mod secp256k1_program_enabled {
solana_sdk::declare_id!("E3PHP7w8kB7np3CTQ1qQ2tW3KCtjRSXBQgW9vM2mWv2Y");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
(instructions_sysvar_enabled::id(), "instructions sysvar"),
(secp256k1_program_enabled::id(), "secp256k1 program")
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
.cloned()
.collect();
/// Unique identifier of the current software's feature set
pub static ref ID: Hash = {
let mut hasher = Hasher::default();
let mut feature_ids = FEATURE_NAMES.keys().collect::<Vec<_>>();
feature_ids.sort();
for feature in feature_ids {
hasher.hash(feature.as_ref());
}
hasher.result()
};
}
/// `FeatureSet` holds the set of currently active/inactive runtime features
#[derive(AbiExample, Clone)]
pub struct FeatureSet {
pub active: HashSet<Pubkey>,
pub inactive: HashSet<Pubkey>,
}
impl FeatureSet {
pub fn is_active(&self, feature_id: &Pubkey) -> bool {
self.active.contains(feature_id)
}
}
impl Default for FeatureSet {
fn default() -> Self {
// All features disabled
Self {
active: HashSet::new(),
inactive: FEATURE_NAMES.keys().cloned().collect(),
}
}
}
impl FeatureSet {
pub fn enabled() -> Self {
Self {
active: FEATURE_NAMES.keys().cloned().collect(),
inactive: HashSet::new(),
}
}
}

View File

@@ -1,7 +1,8 @@
use crate::{feature::Feature, feature_set::FeatureSet};
use solana_sdk::{
account::Account,
fee_calculator::FeeRateGovernor,
genesis_config::GenesisConfig,
genesis_config::{ClusterType, GenesisConfig},
pubkey::Pubkey,
rent::Rent,
signature::{Keypair, Signer},
@@ -107,6 +108,24 @@ pub fn create_genesis_config_with_leader(
)
}
pub fn add_feature_accounts(genesis_config: &mut GenesisConfig) {
if genesis_config.cluster_type == ClusterType::Development {
// Activate all features at genesis in development mode
for feature_id in FeatureSet::default().inactive {
let feature = Feature {
activated_at: Some(0),
};
genesis_config.accounts.insert(
feature_id,
feature.create_account(std::cmp::max(
genesis_config.rent.minimum_balance(Feature::size_of()),
1,
)),
);
}
}
}
pub fn create_genesis_config_with_leader_ex(
mint_lamports: u64,
bootstrap_validator_pubkey: &Pubkey,
@@ -164,6 +183,7 @@ pub fn create_genesis_config_with_leader_ex(
};
solana_stake_program::add_genesis_accounts(&mut genesis_config);
add_feature_accounts(&mut genesis_config);
GenesisConfigInfo {
genesis_config,

View File

@@ -12,6 +12,8 @@ pub mod bloom;
pub mod builtins;
pub mod commitment;
pub mod epoch_stakes;
pub mod feature;
pub mod feature_set;
pub mod genesis_utils;
pub mod hardened_unpack;
pub mod instruction_recorder;

View File

@@ -1,6 +1,9 @@
use crate::{
instruction_recorder::InstructionRecorder, log_collector::LogCollector,
native_loader::NativeLoader, rent_collector::RentCollector,
feature_set::{self, FeatureSet},
instruction_recorder::InstructionRecorder,
log_collector::LogCollector,
native_loader::NativeLoader,
rent_collector::RentCollector,
};
use log::*;
use serde::{Deserialize, Serialize};
@@ -11,7 +14,6 @@ use solana_sdk::{
ComputeBudget, ComputeMeter, ErasedProcessInstruction, ErasedProcessInstructionWithContext,
Executor, InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext,
},
genesis_config::ClusterType,
instruction::{CompiledInstruction, Instruction, InstructionError},
message::Message,
native_loader,
@@ -679,12 +681,11 @@ impl MessageProcessor {
executors: Rc<RefCell<Executors>>,
instruction_recorder: Option<InstructionRecorder>,
instruction_index: usize,
cluster_type: ClusterType,
epoch: Epoch,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
// Fixup the special instructions key if present
// before the account pre-values are taken care of
if solana_sdk::sysvar::instructions::is_enabled(epoch, cluster_type) {
if feature_set.is_active(&feature_set::instructions_sysvar_enabled::id()) {
for (i, key) in message.account_keys.iter().enumerate() {
if solana_sdk::sysvar::instructions::check_id(key) {
let mut mut_account_ref = accounts[i].borrow_mut();
@@ -736,8 +737,7 @@ impl MessageProcessor {
log_collector: Option<Rc<LogCollector>>,
executors: Rc<RefCell<Executors>>,
instruction_recorders: Option<&[InstructionRecorder]>,
cluster_type: ClusterType,
epoch: Epoch,
feature_set: &FeatureSet,
) -> Result<(), TransactionError> {
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let instruction_recorder = instruction_recorders
@@ -753,8 +753,7 @@ impl MessageProcessor {
executors.clone(),
instruction_recorder,
instruction_index,
cluster_type,
epoch,
feature_set,
)
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
}
@@ -1349,8 +1348,7 @@ mod tests {
None,
executors.clone(),
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 100);
@@ -1373,8 +1371,7 @@ mod tests {
None,
executors.clone(),
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(
result,
@@ -1401,8 +1398,7 @@ mod tests {
None,
executors,
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(
result,
@@ -1512,8 +1508,7 @@ mod tests {
None,
executors.clone(),
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(
result,
@@ -1540,8 +1535,7 @@ mod tests {
None,
executors.clone(),
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(result, Ok(()));
@@ -1565,8 +1559,7 @@ mod tests {
None,
executors,
None,
ClusterType::Development,
0,
&FeatureSet::default(),
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].borrow().lamports, 80);