Remove support for dynamically loaded native programs (#20444)
This commit is contained in:
parent
a98aefa14e
commit
785fcb63f5
|
@ -4730,14 +4730,6 @@ dependencies = [
|
|||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-failure-program"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-faucet"
|
||||
version = "1.8.0"
|
||||
|
@ -5163,15 +5155,6 @@ dependencies = [
|
|||
"url 2.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-noop-program"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"log 0.4.14",
|
||||
"solana-logger 1.8.0",
|
||||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-notifier"
|
||||
version = "1.8.0"
|
||||
|
@ -5181,18 +5164,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-ownable"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-perf"
|
||||
version = "1.8.0"
|
||||
|
|
|
@ -49,9 +49,6 @@ members = [
|
|||
"programs/bpf_loader",
|
||||
"programs/compute-budget",
|
||||
"programs/config",
|
||||
"programs/failure",
|
||||
"programs/noop",
|
||||
"programs/ownable",
|
||||
"programs/stake",
|
||||
"programs/vote",
|
||||
"rbpf-cli",
|
||||
|
|
|
@ -4,7 +4,9 @@ use solana_sdk::{
|
|||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
feature_set::{demote_program_write_locks, do_support_realloc, fix_write_privs},
|
||||
feature_set::{
|
||||
demote_program_write_locks, do_support_realloc, fix_write_privs, remove_native_loader,
|
||||
},
|
||||
ic_msg,
|
||||
instruction::{Instruction, InstructionError},
|
||||
message::Message,
|
||||
|
@ -364,12 +366,14 @@ impl InstructionProcessor {
|
|||
return process_instruction(program_id, instruction_data, invoke_context);
|
||||
}
|
||||
}
|
||||
// Call the program via the native loader
|
||||
return self.native_loader.process_instruction(
|
||||
&solana_sdk::native_loader::id(),
|
||||
instruction_data,
|
||||
invoke_context,
|
||||
);
|
||||
if !invoke_context.is_feature_active(&remove_native_loader::id()) {
|
||||
// Call the program via the native loader
|
||||
return self.native_loader.process_instruction(
|
||||
&solana_sdk::native_loader::id(),
|
||||
instruction_data,
|
||||
invoke_context,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let owner_id = &root_account.owner()?;
|
||||
for (id, process_instruction) in &self.programs {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
[package]
|
||||
name = "solana-failure-program"
|
||||
version = "1.8.0"
|
||||
description = "Solana failure program"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-failure-program"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "solana_failure_program"
|
||||
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -1,17 +0,0 @@
|
|||
use solana_sdk::{
|
||||
instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
solana_sdk::declare_program!(
|
||||
"FaiLure111111111111111111111111111111111111",
|
||||
solana_failure_program,
|
||||
process_instruction
|
||||
);
|
||||
|
||||
fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_data: &[u8],
|
||||
_invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
Err(InstructionError::Custom(0))
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_runtime::loader_utils::create_invoke_instruction;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::genesis_config::create_genesis_config;
|
||||
use solana_sdk::instruction::InstructionError;
|
||||
use solana_sdk::signature::Signer;
|
||||
use solana_sdk::transaction::TransactionError;
|
||||
|
||||
#[test]
|
||||
fn test_program_native_failure() {
|
||||
let (genesis_config, alice_keypair) = create_genesis_config(50);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
bank.add_builtin_account("solana_failure_program", &program_id, false);
|
||||
|
||||
// Call user program
|
||||
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
let bank_client = BankClient::new(bank);
|
||||
assert_eq!(
|
||||
bank_client
|
||||
.send_and_confirm_instruction(&alice_keypair, instruction)
|
||||
.unwrap_err()
|
||||
.unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(0))
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "solana-noop-program"
|
||||
version = "1.8.0"
|
||||
description = "Solana Noop program"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-noop-program"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
solana-logger = { path = "../../logger", version = "=1.8.0" }
|
||||
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "solana_noop_program"
|
||||
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -1,21 +0,0 @@
|
|||
use log::*;
|
||||
use solana_sdk::{
|
||||
instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
solana_sdk::declare_program!(
|
||||
"Noop111111111111111111111111111111111111111",
|
||||
solana_noop_program,
|
||||
process_instruction
|
||||
);
|
||||
|
||||
pub fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
data: &[u8],
|
||||
_invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
solana_logger::setup();
|
||||
trace!("noop: program_id: {:?}", program_id);
|
||||
trace!("noop: data: {:?}", data);
|
||||
Ok(())
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
[package]
|
||||
name = "solana-ownable"
|
||||
version = "1.8.0"
|
||||
description = "ownable program"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-ownable"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "solana_ownable"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -1,4 +0,0 @@
|
|||
pub mod ownable_instruction;
|
||||
pub mod ownable_processor;
|
||||
|
||||
solana_sdk::declare_id!("ownab1e111111111111111111111111111111111111");
|
|
@ -1,52 +0,0 @@
|
|||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use solana_sdk::{
|
||||
decode_error::DecodeError,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
pubkey::Pubkey,
|
||||
system_instruction,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
pub enum OwnableError {
|
||||
#[error("incorrect error")]
|
||||
IncorrectOwner,
|
||||
}
|
||||
|
||||
impl<T> DecodeError<T> for OwnableError {
|
||||
fn type_of() -> &'static str {
|
||||
"OwnableError"
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_account(account_pubkey: &Pubkey, owner_pubkey: &Pubkey) -> Instruction {
|
||||
let keys = vec![AccountMeta::new(*account_pubkey, false)];
|
||||
Instruction::new_with_bincode(crate::id(), &owner_pubkey, keys)
|
||||
}
|
||||
|
||||
pub fn create_account(
|
||||
payer_pubkey: &Pubkey,
|
||||
account_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
let space = std::mem::size_of::<Pubkey>() as u64;
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
payer_pubkey,
|
||||
account_pubkey,
|
||||
lamports,
|
||||
space,
|
||||
&crate::id(),
|
||||
),
|
||||
initialize_account(account_pubkey, owner_pubkey),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_owner(account_pubkey: &Pubkey, old_pubkey: &Pubkey, new_pubkey: &Pubkey) -> Instruction {
|
||||
let keys = vec![
|
||||
AccountMeta::new(*account_pubkey, false),
|
||||
AccountMeta::new(*old_pubkey, true),
|
||||
];
|
||||
Instruction::new_with_bincode(crate::id(), &new_pubkey, keys)
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
//! Ownable program
|
||||
|
||||
use crate::ownable_instruction::OwnableError;
|
||||
use bincode::serialize_into;
|
||||
use solana_sdk::{
|
||||
account::{ReadableAccount, WritableAccount},
|
||||
instruction::InstructionError,
|
||||
keyed_account::{keyed_account_at_index, KeyedAccount},
|
||||
process_instruction::InvokeContext,
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
fn set_owner(
|
||||
account_owner_pubkey: &mut Pubkey,
|
||||
new_owner_pubkey: Pubkey,
|
||||
owner_keyed_account: &KeyedAccount,
|
||||
) -> Result<(), InstructionError> {
|
||||
match owner_keyed_account.signer_key() {
|
||||
None => return Err(InstructionError::MissingRequiredSignature),
|
||||
Some(signer_key) => {
|
||||
if account_owner_pubkey != signer_key {
|
||||
return Err(OwnableError::IncorrectOwner.into());
|
||||
}
|
||||
*account_owner_pubkey = new_owner_pubkey;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
data: &[u8],
|
||||
invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||
|
||||
let new_owner_pubkey: Pubkey = limited_deserialize(data)?;
|
||||
let account_keyed_account = &mut keyed_account_at_index(keyed_accounts, 0)?;
|
||||
let mut account_owner_pubkey: Pubkey =
|
||||
limited_deserialize(account_keyed_account.try_account_ref()?.data())?;
|
||||
|
||||
if account_owner_pubkey == Pubkey::default() {
|
||||
account_owner_pubkey = new_owner_pubkey;
|
||||
} else {
|
||||
let owner_keyed_account = &mut keyed_account_at_index(keyed_accounts, 1)?;
|
||||
set_owner(
|
||||
&mut account_owner_pubkey,
|
||||
new_owner_pubkey,
|
||||
owner_keyed_account,
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut account = account_keyed_account.try_account_ref_mut()?;
|
||||
serialize_into(account.data_as_mut_slice(), &account_owner_pubkey)
|
||||
.map_err(|_| InstructionError::AccountDataTooSmall)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ownable_instruction;
|
||||
use solana_runtime::{bank::Bank, bank_client::BankClient};
|
||||
use solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
client::SyncClient,
|
||||
genesis_config::create_genesis_config,
|
||||
message::Message,
|
||||
signature::{Keypair, Signature, Signer},
|
||||
system_program,
|
||||
transport::Result,
|
||||
};
|
||||
|
||||
fn create_bank(lamports: u64) -> (Bank, Keypair) {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(lamports);
|
||||
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||
bank.add_builtin("ownable_program", &crate::id(), process_instruction);
|
||||
(bank, mint_keypair)
|
||||
}
|
||||
|
||||
fn create_bank_client(lamports: u64) -> (BankClient, Keypair) {
|
||||
let (bank, mint_keypair) = create_bank(lamports);
|
||||
(BankClient::new(bank), mint_keypair)
|
||||
}
|
||||
|
||||
fn create_ownable_account(
|
||||
bank_client: &BankClient,
|
||||
payer_keypair: &Keypair,
|
||||
account_keypair: &Keypair,
|
||||
owner_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Result<Signature> {
|
||||
let instructions = ownable_instruction::create_account(
|
||||
&payer_keypair.pubkey(),
|
||||
&account_keypair.pubkey(),
|
||||
owner_pubkey,
|
||||
lamports,
|
||||
);
|
||||
let message = Message::new(&instructions, Some(&payer_keypair.pubkey()));
|
||||
bank_client.send_and_confirm_message(&[payer_keypair, account_keypair], message)
|
||||
}
|
||||
|
||||
fn send_set_owner(
|
||||
bank_client: &BankClient,
|
||||
payer_keypair: &Keypair,
|
||||
account_pubkey: &Pubkey,
|
||||
old_owner_keypair: &Keypair,
|
||||
new_owner_pubkey: &Pubkey,
|
||||
) -> Result<Signature> {
|
||||
let instruction = ownable_instruction::set_owner(
|
||||
account_pubkey,
|
||||
&old_owner_keypair.pubkey(),
|
||||
new_owner_pubkey,
|
||||
);
|
||||
let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
|
||||
bank_client.send_and_confirm_message(&[payer_keypair, old_owner_keypair], message)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ownable_set_owner() {
|
||||
let (bank_client, payer_keypair) = create_bank_client(2);
|
||||
let account_keypair = Keypair::new();
|
||||
let account_pubkey = account_keypair.pubkey();
|
||||
let owner_keypair = Keypair::new();
|
||||
let owner_pubkey = owner_keypair.pubkey();
|
||||
|
||||
create_ownable_account(
|
||||
&bank_client,
|
||||
&payer_keypair,
|
||||
&account_keypair,
|
||||
&owner_pubkey,
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let new_owner_keypair = Keypair::new();
|
||||
let new_owner_pubkey = new_owner_keypair.pubkey();
|
||||
send_set_owner(
|
||||
&bank_client,
|
||||
&payer_keypair,
|
||||
&account_pubkey,
|
||||
&owner_keypair,
|
||||
&new_owner_pubkey,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let account_data = bank_client
|
||||
.get_account_data(&account_pubkey)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let account_owner_pubkey: Pubkey = limited_deserialize(&account_data).unwrap();
|
||||
assert_eq!(account_owner_pubkey, new_owner_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ownable_missing_owner_signature() {
|
||||
let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let owner_pubkey = account_owner_pubkey;
|
||||
let new_owner_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let account = AccountSharedData::new_ref(1, 0, &system_program::id());
|
||||
let owner_keyed_account = KeyedAccount::new(&owner_pubkey, false, &account); // <-- Attack! Setting owner without the original owner's signature.
|
||||
let err = set_owner(
|
||||
&mut account_owner_pubkey,
|
||||
new_owner_pubkey,
|
||||
&owner_keyed_account,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(err, InstructionError::MissingRequiredSignature);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ownable_incorrect_owner() {
|
||||
let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let new_owner_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let account = AccountSharedData::new_ref(1, 0, &system_program::id());
|
||||
let mallory_pubkey = solana_sdk::pubkey::new_rand(); // <-- Attack! Signing with wrong pubkey
|
||||
let owner_keyed_account = KeyedAccount::new(&mallory_pubkey, true, &account);
|
||||
let err = set_owner(
|
||||
&mut account_owner_pubkey,
|
||||
new_owner_pubkey,
|
||||
&owner_keyed_account,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(err, OwnableError::IncorrectOwner.into());
|
||||
}
|
||||
}
|
|
@ -6293,7 +6293,6 @@ pub(crate) mod tests {
|
|||
status_cache::MAX_CACHE_ENTRIES,
|
||||
};
|
||||
use crossbeam_channel::{bounded, unbounded};
|
||||
use solana_program_runtime::NativeLoaderError;
|
||||
#[allow(deprecated)]
|
||||
use solana_sdk::sysvar::fees::Fees;
|
||||
use solana_sdk::{
|
||||
|
@ -13420,67 +13419,6 @@ pub(crate) mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_native_loader() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(50000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let to_keypair = Keypair::new();
|
||||
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::create_account(
|
||||
&mint_keypair.pubkey(),
|
||||
&to_keypair.pubkey(),
|
||||
10000,
|
||||
0,
|
||||
&native_loader::id(),
|
||||
),
|
||||
Instruction::new_with_bincode(
|
||||
native_loader::id(),
|
||||
&(),
|
||||
vec![AccountMeta::new(to_keypair.pubkey(), false)],
|
||||
),
|
||||
],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair, &to_keypair],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx),
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
InstructionError::Custom(NativeLoaderError::InvalidAccountData as u32)
|
||||
))
|
||||
);
|
||||
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::create_account(
|
||||
&mint_keypair.pubkey(),
|
||||
&to_keypair.pubkey(),
|
||||
10000,
|
||||
100,
|
||||
&native_loader::id(),
|
||||
),
|
||||
Instruction::new_with_bincode(
|
||||
native_loader::id(),
|
||||
&(),
|
||||
vec![AccountMeta::new(to_keypair.pubkey(), false)],
|
||||
),
|
||||
],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair, &to_keypair],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx),
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
InstructionError::Custom(NativeLoaderError::InvalidAccountData as u32)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_bank() {
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(50000);
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
use solana_runtime::{
|
||||
bank::Bank, bank_client::BankClient, loader_utils::create_invoke_instruction,
|
||||
};
|
||||
use solana_sdk::{client::SyncClient, genesis_config::create_genesis_config, signature::Signer};
|
||||
|
||||
#[test]
|
||||
fn test_program_native_noop() {
|
||||
solana_logger::setup();
|
||||
|
||||
let (genesis_config, alice_keypair) = create_genesis_config(50);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
bank.add_builtin_account("solana_noop_program", &program_id, false);
|
||||
|
||||
// Call user program
|
||||
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
let bank_client = BankClient::new(bank);
|
||||
bank_client
|
||||
.send_and_confirm_instruction(&alice_keypair, instruction)
|
||||
.unwrap();
|
||||
}
|
|
@ -233,6 +233,10 @@ pub mod optimize_epoch_boundary_updates {
|
|||
solana_sdk::declare_id!("265hPS8k8xJ37ot82KEgjRunsUp5w4n4Q4VwwiN9i9ps");
|
||||
}
|
||||
|
||||
pub mod remove_native_loader {
|
||||
solana_sdk::declare_id!("HTTgmruMYRZEntyL3EdCDdnS6e4D5wRq1FA7kQsb66qq");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -286,6 +290,7 @@ lazy_static! {
|
|||
(do_support_realloc::id(), "support account data reallocation"),
|
||||
(prevent_calling_precompiles_as_programs::id(), "Prevent calling precompiles as programs"),
|
||||
(optimize_epoch_boundary_updates::id(), "Optimize epoch boundary updates"),
|
||||
(remove_native_loader::id(), "Remove support for the native loader"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
Loading…
Reference in New Issue