Dont call precompiled programs (#19930)
This commit is contained in:
@ -34,7 +34,6 @@ serde = { version = "1.0.130", features = ["rc"] }
|
||||
serde_derive = "1.0.103"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.8.0" }
|
||||
solana-compute-budget-program = { path = "../programs/compute-budget", version = "=1.8.0" }
|
||||
solana-ed25519-program = { path = "../programs/ed25519", version = "=1.8.0" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.8.0" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.8.0" }
|
||||
solana-logger = { path = "../logger", version = "=1.8.0" }
|
||||
@ -44,7 +43,6 @@ solana-bucket-map = { path = "../bucket_map", version = "=1.8.0" }
|
||||
solana-program-runtime = { path = "../program-runtime", version = "=1.8.0" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.8.0" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.8.0" }
|
||||
solana-secp256k1-program = { path = "../programs/secp256k1", version = "=1.8.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.8.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.8.0" }
|
||||
symlink = "0.1.0"
|
||||
@ -58,6 +56,8 @@ crate-type = ["lib"]
|
||||
name = "solana_runtime"
|
||||
|
||||
[dev-dependencies]
|
||||
ed25519-dalek = "=1.0.1"
|
||||
libsecp256k1 = "0.6.0"
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@ -127,10 +127,10 @@ fn do_bench_transactions(
|
||||
let mut bank = Bank::new_for_benches(&genesis_config);
|
||||
bank.add_builtin(
|
||||
"builtin_program",
|
||||
Pubkey::new(&BUILTIN_PROGRAM_ID),
|
||||
&Pubkey::new(&BUILTIN_PROGRAM_ID),
|
||||
process_instruction,
|
||||
);
|
||||
bank.add_native_program("solana_noop_program", &Pubkey::new(&NOOP_PROGRAM_ID), false);
|
||||
bank.add_builtin_account("solana_noop_program", &Pubkey::new(&NOOP_PROGRAM_ID), false);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
let transactions = create_transactions(&bank_client, &mint_keypair);
|
||||
|
@ -97,6 +97,7 @@ use solana_sdk::{
|
||||
native_token::sol_to_lamports,
|
||||
nonce, nonce_account,
|
||||
packet::PACKET_DATA_SIZE,
|
||||
precompiles::get_precompiles,
|
||||
process_instruction::{ComputeMeter, Executor, ProcessInstructionWithContext},
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
@ -2500,38 +2501,38 @@ impl Bank {
|
||||
|
||||
// Add additional native programs specified in the genesis config
|
||||
for (name, program_id) in &genesis_config.native_instruction_processors {
|
||||
self.add_native_program(name, program_id, false);
|
||||
self.add_builtin_account(name, program_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn burn_and_purge_account(&self, program_id: &Pubkey, mut account: AccountSharedData) {
|
||||
self.capitalization.fetch_sub(account.lamports(), Relaxed);
|
||||
// Resetting account balance to 0 is needed to really purge from AccountsDb and
|
||||
// flush the Stakes cache
|
||||
account.set_lamports(0);
|
||||
self.store_account(program_id, &account);
|
||||
}
|
||||
|
||||
// NOTE: must hold idempotent for the same set of arguments
|
||||
pub fn add_native_program(&self, name: &str, program_id: &Pubkey, must_replace: bool) {
|
||||
/// Add a builtin program account
|
||||
pub fn add_builtin_account(&self, name: &str, program_id: &Pubkey, must_replace: bool) {
|
||||
let existing_genuine_program =
|
||||
if let Some(mut account) = self.get_account_with_fixed_root(program_id) {
|
||||
// it's very unlikely to be squatted at program_id as non-system account because of burden to
|
||||
// find victim's pubkey/hash. So, when account.owner is indeed native_loader's, it's
|
||||
// safe to assume it's a genuine program.
|
||||
if native_loader::check_id(account.owner()) {
|
||||
Some(account)
|
||||
} else {
|
||||
// malicious account is pre-occupying at program_id
|
||||
// forcibly burn and purge it
|
||||
|
||||
self.capitalization.fetch_sub(account.lamports(), Relaxed);
|
||||
|
||||
// Resetting account balance to 0 is needed to really purge from AccountsDb and
|
||||
// flush the Stakes cache
|
||||
account.set_lamports(0);
|
||||
self.store_account(program_id, &account);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.get_account_with_fixed_root(program_id)
|
||||
.and_then(|account| {
|
||||
// it's very unlikely to be squatted at program_id as non-system account because of burden to
|
||||
// find victim's pubkey/hash. So, when account.owner is indeed native_loader's, it's
|
||||
// safe to assume it's a genuine program.
|
||||
if native_loader::check_id(account.owner()) {
|
||||
Some(account)
|
||||
} else {
|
||||
// malicious account is pre-occupying at program_id
|
||||
self.burn_and_purge_account(program_id, account);
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if must_replace {
|
||||
// updating native program
|
||||
|
||||
match &existing_genuine_program {
|
||||
None => panic!(
|
||||
"There is no account to replace with native program ({}, {}).",
|
||||
@ -2539,29 +2540,16 @@ impl Bank {
|
||||
),
|
||||
Some(account) => {
|
||||
if *name == String::from_utf8_lossy(account.data()) {
|
||||
// nop; it seems that already AccountsDb is updated.
|
||||
// The existing account is well formed
|
||||
return;
|
||||
}
|
||||
// continue to replace account
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// introducing native program
|
||||
|
||||
match &existing_genuine_program {
|
||||
None => (), // continue to add account
|
||||
Some(_account) => {
|
||||
// nop; it seems that we already have account
|
||||
|
||||
// before returning here to retain idempotent just make sure
|
||||
// the existing native program name is same with what we're
|
||||
// supposed to add here (but skipping) But I can't:
|
||||
// following assertion already catches several different names for same
|
||||
// program_id
|
||||
// depending on clusters...
|
||||
// assert_eq!(name.to_owned(), String::from_utf8_lossy(&account.data));
|
||||
return;
|
||||
}
|
||||
if existing_genuine_program.is_some() {
|
||||
// The existing account is sufficient
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2579,8 +2567,37 @@ impl Bank {
|
||||
self.inherit_specially_retained_account_fields(&existing_genuine_program),
|
||||
);
|
||||
self.store_account_and_update_capitalization(program_id, &account);
|
||||
}
|
||||
|
||||
debug!("Added native program {} under {:?}", name, program_id);
|
||||
/// Add a precompiled program account
|
||||
pub fn add_precompiled_account(&self, program_id: &Pubkey) {
|
||||
if let Some(account) = self.get_account_with_fixed_root(program_id) {
|
||||
if account.executable() {
|
||||
// The account is already executable, that's all we need
|
||||
return;
|
||||
} else {
|
||||
// malicious account is pre-occupying at program_id
|
||||
self.burn_and_purge_account(program_id, account);
|
||||
}
|
||||
};
|
||||
|
||||
assert!(
|
||||
!self.freeze_started(),
|
||||
"Can't change frozen bank by adding not-existing new precompiled program ({}). \
|
||||
Maybe, inconsistent program activation is detected on snapshot restore?",
|
||||
program_id
|
||||
);
|
||||
|
||||
// Add a bogus executable account, which will be loaded and ignored.
|
||||
let (lamports, rent_epoch) = self.inherit_specially_retained_account_fields(&None);
|
||||
let account = AccountSharedData::from(Account {
|
||||
lamports,
|
||||
owner: solana_sdk::system_program::id(),
|
||||
data: vec![],
|
||||
executable: true,
|
||||
rent_epoch,
|
||||
});
|
||||
self.store_account_and_update_capitalization(program_id, &account);
|
||||
}
|
||||
|
||||
pub fn set_rent_burn_percentage(&mut self, burn_percent: u8) {
|
||||
@ -4587,10 +4604,15 @@ impl Bank {
|
||||
for builtin in builtins.genesis_builtins {
|
||||
self.add_builtin(
|
||||
&builtin.name,
|
||||
builtin.id,
|
||||
&builtin.id,
|
||||
builtin.process_instruction_with_context,
|
||||
);
|
||||
}
|
||||
for precompile in get_precompiles() {
|
||||
if precompile.feature.is_none() {
|
||||
self.add_precompile(&precompile.program_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.feature_builtins = Arc::new(builtins.feature_builtins);
|
||||
|
||||
@ -5320,26 +5342,43 @@ impl Bank {
|
||||
pub fn add_builtin(
|
||||
&mut self,
|
||||
name: &str,
|
||||
program_id: Pubkey,
|
||||
program_id: &Pubkey,
|
||||
process_instruction_with_context: ProcessInstructionWithContext,
|
||||
) {
|
||||
debug!("Adding program {} under {:?}", name, program_id);
|
||||
self.add_native_program(name, &program_id, false);
|
||||
self.add_builtin_account(name, program_id, false);
|
||||
self.message_processor
|
||||
.add_program(program_id, process_instruction_with_context);
|
||||
debug!("Added program {} under {:?}", name, program_id);
|
||||
}
|
||||
|
||||
/// Replace a builtin instruction processor if it already exists
|
||||
pub fn replace_builtin(
|
||||
&mut self,
|
||||
name: &str,
|
||||
program_id: Pubkey,
|
||||
program_id: &Pubkey,
|
||||
process_instruction_with_context: ProcessInstructionWithContext,
|
||||
) {
|
||||
debug!("Replacing program {} under {:?}", name, program_id);
|
||||
self.add_native_program(name, &program_id, true);
|
||||
self.add_builtin_account(name, program_id, true);
|
||||
self.message_processor
|
||||
.add_program(program_id, process_instruction_with_context);
|
||||
debug!("Replaced program {} under {:?}", name, program_id);
|
||||
}
|
||||
|
||||
/// Remove a builtin instruction processor if it already exists
|
||||
pub fn remove_builtin(&mut self, name: &str, program_id: &Pubkey) {
|
||||
debug!("Removing program {} under {:?}", name, program_id);
|
||||
// Don't remove the account since the bank expects the account state to
|
||||
// be idempotent
|
||||
self.message_processor.remove_program(program_id);
|
||||
debug!("Removed program {} under {:?}", name, program_id);
|
||||
}
|
||||
|
||||
pub fn add_precompile(&mut self, program_id: &Pubkey) {
|
||||
debug!("Adding precompiled program {}", program_id);
|
||||
self.add_precompiled_account(program_id);
|
||||
debug!("Added precompiled program {:?}", program_id);
|
||||
}
|
||||
|
||||
pub fn clean_accounts(
|
||||
@ -5610,17 +5649,28 @@ impl Bank {
|
||||
match activation_type {
|
||||
ActivationType::NewProgram => self.add_builtin(
|
||||
&builtin.name,
|
||||
builtin.id,
|
||||
&builtin.id,
|
||||
builtin.process_instruction_with_context,
|
||||
),
|
||||
ActivationType::NewVersion => self.replace_builtin(
|
||||
&builtin.name,
|
||||
builtin.id,
|
||||
&builtin.id,
|
||||
builtin.process_instruction_with_context,
|
||||
),
|
||||
ActivationType::RemoveProgram => {
|
||||
self.remove_builtin(&builtin.name, &builtin.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for precompile in get_precompiles() {
|
||||
#[allow(clippy::blocks_in_if_conditions)]
|
||||
if precompile.feature.map_or(false, |ref feature_id| {
|
||||
self.feature_set.is_active(feature_id)
|
||||
}) {
|
||||
self.add_precompile(&precompile.program_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_spl_token_v2_set_authority_fix(&mut self) {
|
||||
@ -6342,7 +6392,7 @@ pub(crate) mod tests {
|
||||
) as u64,
|
||||
);
|
||||
bank.rent_collector.slots_per_year = 421_812.0;
|
||||
bank.add_builtin("mock_program", mock_program_id, mock_process_instruction);
|
||||
bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction);
|
||||
|
||||
bank
|
||||
}
|
||||
@ -9828,7 +9878,7 @@ pub(crate) mod tests {
|
||||
assert!(bank.get_account(&mock_vote_program_id()).is_none());
|
||||
bank.add_builtin(
|
||||
"mock_vote_program",
|
||||
mock_vote_program_id(),
|
||||
&mock_vote_program_id(),
|
||||
mock_vote_processor,
|
||||
);
|
||||
assert!(bank.get_account(&mock_vote_program_id()).is_some());
|
||||
@ -9901,7 +9951,7 @@ pub(crate) mod tests {
|
||||
let vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap();
|
||||
bank.add_builtin(
|
||||
"solana_vote_program",
|
||||
solana_vote_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
mock_vote_processor,
|
||||
);
|
||||
let new_vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap();
|
||||
@ -9950,8 +10000,8 @@ pub(crate) mod tests {
|
||||
assert!(!bank.stakes.read().unwrap().stake_delegations().is_empty());
|
||||
assert_eq!(bank.calculate_capitalization(true), bank.capitalization());
|
||||
|
||||
bank.add_builtin("mock_program1", vote_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program2", stake_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program1", &vote_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program2", &stake_id, mock_ix_processor);
|
||||
{
|
||||
let stakes = bank.stakes.read().unwrap();
|
||||
assert!(stakes.vote_accounts().as_ref().is_empty());
|
||||
@ -9970,8 +10020,8 @@ pub(crate) mod tests {
|
||||
// Re-adding builtin programs should be no-op
|
||||
bank.update_accounts_hash();
|
||||
let old_hash = bank.get_accounts_hash();
|
||||
bank.add_builtin("mock_program1", vote_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program2", stake_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program1", &vote_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program2", &stake_id, mock_ix_processor);
|
||||
bank.update_accounts_hash();
|
||||
let new_hash = bank.get_accounts_hash();
|
||||
assert_eq!(old_hash, new_hash);
|
||||
@ -10785,7 +10835,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
||||
bank.add_builtin("mock_program", mock_program_id, mock_process_instruction);
|
||||
bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction);
|
||||
|
||||
let from_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||
@ -10829,7 +10879,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
||||
bank.add_builtin("mock_program", mock_program_id, mock_process_instruction);
|
||||
bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction);
|
||||
|
||||
let from_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||
@ -10884,7 +10934,7 @@ pub(crate) mod tests {
|
||||
|
||||
bank.add_builtin(
|
||||
"mock_vote",
|
||||
solana_vote_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
mock_ok_vote_processor,
|
||||
);
|
||||
let result = bank.process_transaction(&tx);
|
||||
@ -10938,7 +10988,7 @@ pub(crate) mod tests {
|
||||
|
||||
bank.add_builtin(
|
||||
"mock_vote",
|
||||
solana_vote_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
mock_ok_vote_processor,
|
||||
);
|
||||
|
||||
@ -10972,7 +11022,7 @@ pub(crate) mod tests {
|
||||
|
||||
bank.add_builtin(
|
||||
"mock_vote",
|
||||
solana_vote_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
mock_ok_vote_processor,
|
||||
);
|
||||
|
||||
@ -11029,7 +11079,7 @@ pub(crate) mod tests {
|
||||
|
||||
bank.add_builtin(
|
||||
"mock_vote",
|
||||
solana_vote_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
mock_ok_vote_processor,
|
||||
);
|
||||
|
||||
@ -11064,7 +11114,7 @@ pub(crate) mod tests {
|
||||
.map(|i| {
|
||||
let key = solana_sdk::pubkey::new_rand();
|
||||
let name = format!("program{:?}", i);
|
||||
bank.add_builtin(&name, key, mock_ok_vote_processor);
|
||||
bank.add_builtin(&name, &key, mock_ok_vote_processor);
|
||||
(key, name.as_bytes().to_vec())
|
||||
})
|
||||
.collect();
|
||||
@ -11273,7 +11323,7 @@ pub(crate) mod tests {
|
||||
|
||||
// Add a new program
|
||||
let program1_pubkey = solana_sdk::pubkey::new_rand();
|
||||
bank.add_builtin("program", program1_pubkey, nested_processor);
|
||||
bank.add_builtin("program", &program1_pubkey, nested_processor);
|
||||
|
||||
// Add a new program owned by the first
|
||||
let program2_pubkey = solana_sdk::pubkey::new_rand();
|
||||
@ -11640,20 +11690,24 @@ pub(crate) mod tests {
|
||||
));
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id), None);
|
||||
|
||||
Arc::get_mut(&mut bank)
|
||||
.unwrap()
|
||||
.add_builtin("mock_program", program_id, mock_ix_processor);
|
||||
Arc::get_mut(&mut bank).unwrap().add_builtin(
|
||||
"mock_program",
|
||||
&program_id,
|
||||
mock_ix_processor,
|
||||
);
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot);
|
||||
|
||||
let mut bank = Arc::new(new_from_parent(&bank));
|
||||
Arc::get_mut(&mut bank)
|
||||
.unwrap()
|
||||
.add_builtin("mock_program", program_id, mock_ix_processor);
|
||||
Arc::get_mut(&mut bank).unwrap().add_builtin(
|
||||
"mock_program",
|
||||
&program_id,
|
||||
mock_ix_processor,
|
||||
);
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot);
|
||||
|
||||
Arc::get_mut(&mut bank).unwrap().replace_builtin(
|
||||
"mock_program v2",
|
||||
program_id,
|
||||
&program_id,
|
||||
mock_ix_processor,
|
||||
);
|
||||
assert_eq!(
|
||||
@ -11687,18 +11741,18 @@ pub(crate) mod tests {
|
||||
|
||||
Arc::get_mut(&mut bank)
|
||||
.unwrap()
|
||||
.add_builtin("mock_program", loader_id, mock_ix_processor);
|
||||
.add_builtin("mock_program", &loader_id, mock_ix_processor);
|
||||
assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot);
|
||||
|
||||
let mut bank = Arc::new(new_from_parent(&bank));
|
||||
Arc::get_mut(&mut bank)
|
||||
.unwrap()
|
||||
.add_builtin("mock_program", loader_id, mock_ix_processor);
|
||||
.add_builtin("mock_program", &loader_id, mock_ix_processor);
|
||||
assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_native_program() {
|
||||
fn test_add_builtin_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
activate_all_features(&mut genesis_config);
|
||||
|
||||
@ -11714,7 +11768,7 @@ pub(crate) mod tests {
|
||||
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_native_program("mock_program", &program_id, false),
|
||||
|| bank.add_builtin_account("mock_program", &program_id, false),
|
||||
|old, new| {
|
||||
assert_eq!(old + 1, new);
|
||||
},
|
||||
@ -11725,7 +11779,7 @@ pub(crate) mod tests {
|
||||
let bank = Arc::new(new_from_parent(&bank));
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_native_program("mock_program", &program_id, false),
|
||||
|| bank.add_builtin_account("mock_program", &program_id, false),
|
||||
|old, new| assert_eq!(old, new),
|
||||
);
|
||||
|
||||
@ -11736,7 +11790,7 @@ pub(crate) mod tests {
|
||||
// invocations.
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_native_program("mock_program v2", &program_id, true),
|
||||
|| bank.add_builtin_account("mock_program v2", &program_id, true),
|
||||
|old, new| assert_eq!(old, new),
|
||||
);
|
||||
|
||||
@ -11748,7 +11802,7 @@ pub(crate) mod tests {
|
||||
let bank = Arc::new(new_from_parent(&bank));
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_native_program("mock_program v2", &program_id, true),
|
||||
|| bank.add_builtin_account("mock_program v2", &program_id, true),
|
||||
|old, new| assert_eq!(old, new),
|
||||
);
|
||||
|
||||
@ -11760,12 +11814,12 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_native_program_inherited_cap_while_replacing() {
|
||||
fn test_add_builtin_account_inherited_cap_while_replacing() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(100_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
||||
bank.add_native_program("mock_program", &program_id, false);
|
||||
bank.add_builtin_account("mock_program", &program_id, false);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
// someone mess with program_id's balance
|
||||
@ -11774,12 +11828,12 @@ pub(crate) mod tests {
|
||||
bank.deposit(&program_id, 10).unwrap();
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
bank.add_native_program("mock_program v2", &program_id, true);
|
||||
bank.add_builtin_account("mock_program v2", &program_id, true);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_native_program_squatted_while_not_replacing() {
|
||||
fn test_add_builtin_account_squatted_while_not_replacing() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(100_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
@ -11790,7 +11844,7 @@ pub(crate) mod tests {
|
||||
bank.deposit(&program_id, 10).unwrap();
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
bank.add_native_program("mock_program", &program_id, false);
|
||||
bank.add_builtin_account("mock_program", &program_id, false);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
}
|
||||
|
||||
@ -11800,7 +11854,7 @@ pub(crate) mod tests {
|
||||
program (mock_program, CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre). \
|
||||
Maybe, inconsistent program activation is detected on snapshot restore?"
|
||||
)]
|
||||
fn test_add_native_program_after_frozen() {
|
||||
fn test_add_builtin_account_after_frozen() {
|
||||
use std::str::FromStr;
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
|
||||
@ -11814,7 +11868,7 @@ pub(crate) mod tests {
|
||||
);
|
||||
bank.freeze();
|
||||
|
||||
bank.add_native_program("mock_program", &program_id, false);
|
||||
bank.add_builtin_account("mock_program", &program_id, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -11822,7 +11876,7 @@ pub(crate) mod tests {
|
||||
expected = "There is no account to replace with native program (mock_program, \
|
||||
CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre)."
|
||||
)]
|
||||
fn test_add_native_program_replace_none() {
|
||||
fn test_add_builtin_account_replace_none() {
|
||||
use std::str::FromStr;
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
|
||||
@ -11835,7 +11889,100 @@ pub(crate) mod tests {
|
||||
slot,
|
||||
);
|
||||
|
||||
bank.add_native_program("mock_program", &program_id, true);
|
||||
bank.add_builtin_account("mock_program", &program_id, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_precompiled_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
activate_all_features(&mut genesis_config);
|
||||
|
||||
let slot = 123;
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
||||
let bank = Arc::new(Bank::new_from_parent(
|
||||
&Arc::new(Bank::new_for_tests(&genesis_config)),
|
||||
&Pubkey::default(),
|
||||
slot,
|
||||
));
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id), None);
|
||||
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_precompiled_account(&program_id),
|
||||
|old, new| {
|
||||
assert_eq!(old + 1, new);
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot);
|
||||
|
||||
let bank = Arc::new(new_from_parent(&bank));
|
||||
assert_capitalization_diff(
|
||||
&bank,
|
||||
|| bank.add_precompiled_account(&program_id),
|
||||
|old, new| assert_eq!(old, new),
|
||||
);
|
||||
|
||||
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_precompiled_account_inherited_cap_while_replacing() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(100_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
||||
bank.add_precompiled_account(&program_id);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
// someone mess with program_id's balance
|
||||
bank.withdraw(&mint_keypair.pubkey(), 10).unwrap();
|
||||
assert_ne!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
bank.deposit(&program_id, 10).unwrap();
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
bank.add_precompiled_account(&program_id);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_precompiled_account_squatted_while_not_replacing() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(100_000);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
||||
// someone managed to squat at program_id!
|
||||
bank.withdraw(&mint_keypair.pubkey(), 10).unwrap();
|
||||
assert_ne!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
bank.deposit(&program_id, 10).unwrap();
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
|
||||
bank.add_precompiled_account(&program_id);
|
||||
assert_eq!(bank.capitalization(), bank.calculate_capitalization(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Can't change frozen bank by adding not-existing new precompiled \
|
||||
program (CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre). \
|
||||
Maybe, inconsistent program activation is detected on snapshot restore?"
|
||||
)]
|
||||
fn test_add_precompiled_account_after_frozen() {
|
||||
use std::str::FromStr;
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
|
||||
let slot = 123;
|
||||
let program_id = Pubkey::from_str("CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre").unwrap();
|
||||
|
||||
let bank = Bank::new_from_parent(
|
||||
&Arc::new(Bank::new_for_tests(&genesis_config)),
|
||||
&Pubkey::default(),
|
||||
slot,
|
||||
);
|
||||
bank.freeze();
|
||||
|
||||
bank.add_precompiled_account(&program_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -14032,7 +14179,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
bank.add_builtin("mock_program1", program_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program1", &program_id, mock_ix_processor);
|
||||
|
||||
let blockhash = bank.last_blockhash();
|
||||
#[allow(deprecated)]
|
||||
@ -14244,7 +14391,7 @@ pub(crate) mod tests {
|
||||
Ok(())
|
||||
}
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
bank.add_builtin("mock_program", program_id, mock_ix_processor);
|
||||
bank.add_builtin("mock_program", &program_id, mock_ix_processor);
|
||||
|
||||
let message = Message::new(
|
||||
&[
|
||||
@ -14397,4 +14544,47 @@ pub(crate) mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_precomiled_program() {
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config_with_leader(42, &Pubkey::new_unique(), 42);
|
||||
activate_all_features(&mut genesis_config);
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
|
||||
// libsecp256k1
|
||||
let secp_privkey = libsecp256k1::SecretKey::random(&mut rand::thread_rng());
|
||||
let message_arr = b"hello";
|
||||
let instruction = solana_sdk::secp256k1_instruction::new_secp256k1_instruction(
|
||||
&secp_privkey,
|
||||
message_arr,
|
||||
);
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
// calling the program should be successful when called from the bank
|
||||
// even if the program itself is not called
|
||||
bank.process_transaction(&tx).unwrap();
|
||||
|
||||
// ed25519
|
||||
let privkey = ed25519_dalek::Keypair::generate(&mut rand::thread_rng());
|
||||
let message_arr = b"hello";
|
||||
let instruction =
|
||||
solana_sdk::ed25519_instruction::new_ed25519_instruction(&privkey, message_arr);
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&[instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
&[&mint_keypair],
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
// calling the program should be successful when called from the bank
|
||||
// even if the program itself is not called
|
||||
bank.process_transaction(&tx).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ macro_rules! with_program_logging {
|
||||
pub enum ActivationType {
|
||||
NewProgram,
|
||||
NewVersion,
|
||||
RemoveProgram,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -91,7 +92,7 @@ pub struct Builtins {
|
||||
/// Builtin programs that are always available
|
||||
pub genesis_builtins: Vec<Builtin>,
|
||||
|
||||
/// Builtin programs activated dynamically by feature
|
||||
/// Builtin programs activated or deactivated dynamically by feature
|
||||
pub feature_builtins: Vec<(Builtin, Pubkey, ActivationType)>,
|
||||
}
|
||||
|
||||
@ -121,11 +122,20 @@ fn genesis_builtins() -> Vec<Builtin> {
|
||||
Builtin::new(
|
||||
"secp256k1_program",
|
||||
solana_sdk::secp256k1_program::id(),
|
||||
solana_secp256k1_program::process_instruction,
|
||||
dummy_process_instruction,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// place holder for secp256k1, remove when the precompile program is deactivated via feature activation
|
||||
fn dummy_process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_data: &[u8],
|
||||
_invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builtin programs activated dynamically by feature
|
||||
///
|
||||
/// Note: If the feature_builtin is intended to replace another builtin program, it must have a new
|
||||
@ -145,14 +155,17 @@ fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> {
|
||||
feature_set::tx_wide_compute_cap::id(),
|
||||
ActivationType::NewProgram,
|
||||
),
|
||||
// TODO when feature `prevent_calling_precompiles_as_programs` is
|
||||
// cleaned up also remove "secp256k1_program" from the main builtins
|
||||
// list
|
||||
(
|
||||
Builtin::new(
|
||||
"ed25519_program",
|
||||
solana_sdk::ed25519_program::id(),
|
||||
solana_ed25519_program::process_instruction,
|
||||
"secp256k1_program",
|
||||
solana_sdk::secp256k1_program::id(),
|
||||
dummy_process_instruction,
|
||||
),
|
||||
feature_set::ed25519_program_enabled::id(),
|
||||
ActivationType::NewProgram,
|
||||
feature_set::prevent_calling_precompiles_as_programs::id(),
|
||||
ActivationType::RemoveProgram,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
||||
compute_budget::ComputeBudget,
|
||||
feature_set::{
|
||||
demote_program_write_locks, do_support_realloc, neon_evm_compute_budget,
|
||||
tx_wide_compute_cap, FeatureSet,
|
||||
prevent_calling_precompiles_as_programs, tx_wide_compute_cap, FeatureSet,
|
||||
},
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
@ -19,6 +19,7 @@ use solana_sdk::{
|
||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
||||
keyed_account::{create_keyed_accounts_unified, KeyedAccount},
|
||||
message::Message,
|
||||
precompiles::is_precompile,
|
||||
process_instruction::{
|
||||
ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger,
|
||||
ProcessInstructionWithContext,
|
||||
@ -400,13 +401,18 @@ impl MessageProcessor {
|
||||
/// Add a static entrypoint to intercept instructions before the dynamic loader.
|
||||
pub fn add_program(
|
||||
&mut self,
|
||||
program_id: Pubkey,
|
||||
program_id: &Pubkey,
|
||||
process_instruction: ProcessInstructionWithContext,
|
||||
) {
|
||||
self.instruction_processor
|
||||
.add_program(program_id, process_instruction);
|
||||
}
|
||||
|
||||
/// Remove a program.
|
||||
pub fn remove_program(&mut self, program_id: &Pubkey) {
|
||||
self.instruction_processor.remove_program(program_id);
|
||||
}
|
||||
|
||||
/// Record the initial state of the accounts so that they can be compared
|
||||
/// after the instruction is processed
|
||||
pub fn create_pre_accounts(
|
||||
@ -531,6 +537,14 @@ impl MessageProcessor {
|
||||
blockhash: &Hash,
|
||||
fee_calculator: &FeeCalculator,
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = instruction.program_id(&message.account_keys);
|
||||
if feature_set.is_active(&prevent_calling_precompiles_as_programs::id())
|
||||
&& is_precompile(program_id, |id| feature_set.is_active(id))
|
||||
{
|
||||
// Precompiled programs don't have an instruction processor
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Fixup the special instructions key if present
|
||||
// before the account pre-values are taken care of
|
||||
for (pubkey, accont) in accounts.iter().take(message.account_keys.len()) {
|
||||
@ -672,6 +686,8 @@ mod tests {
|
||||
message::Message,
|
||||
native_loader::{self, create_loadable_account_for_test},
|
||||
process_instruction::MockComputeMeter,
|
||||
secp256k1_instruction::new_secp256k1_instruction,
|
||||
secp256k1_program,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -864,7 +880,7 @@ mod tests {
|
||||
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
|
||||
let rent_collector = RentCollector::default();
|
||||
let mut message_processor = MessageProcessor::default();
|
||||
message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
|
||||
message_processor.add_program(&mock_system_program_id, mock_system_process_instruction);
|
||||
|
||||
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
||||
"mock_system_program",
|
||||
@ -1052,7 +1068,7 @@ mod tests {
|
||||
let mock_program_id = Pubkey::new(&[2u8; 32]);
|
||||
let rent_collector = RentCollector::default();
|
||||
let mut message_processor = MessageProcessor::default();
|
||||
message_processor.add_program(mock_program_id, mock_system_process_instruction);
|
||||
message_processor.add_program(&mock_program_id, mock_system_process_instruction);
|
||||
|
||||
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
|
||||
"mock_system_program",
|
||||
@ -1579,4 +1595,63 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precompile() {
|
||||
let mut message_processor = MessageProcessor::default();
|
||||
let mock_program_id = Pubkey::new_unique();
|
||||
fn mock_process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_data: &[u8],
|
||||
_invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
Err(InstructionError::Custom(0xbabb1e))
|
||||
}
|
||||
message_processor.add_program(&mock_program_id, mock_process_instruction);
|
||||
|
||||
let secp256k1_account = AccountSharedData::new_ref(1, 0, &native_loader::id());
|
||||
secp256k1_account.borrow_mut().set_executable(true);
|
||||
let mock_program_account = AccountSharedData::new_ref(1, 0, &native_loader::id());
|
||||
mock_program_account.borrow_mut().set_executable(true);
|
||||
let accounts = vec![
|
||||
(secp256k1_program::id(), secp256k1_account),
|
||||
(mock_program_id, mock_program_account),
|
||||
];
|
||||
|
||||
let message = Message::new(
|
||||
&[
|
||||
new_secp256k1_instruction(
|
||||
&libsecp256k1::SecretKey::random(&mut rand::thread_rng()),
|
||||
b"hello",
|
||||
),
|
||||
Instruction::new_with_bytes(mock_program_id, &[], vec![]),
|
||||
],
|
||||
None,
|
||||
);
|
||||
|
||||
let result = message_processor.process_message(
|
||||
&message,
|
||||
&[vec![0], vec![1]],
|
||||
&accounts,
|
||||
&RentCollector::default(),
|
||||
None,
|
||||
Rc::new(RefCell::new(Executors::default())),
|
||||
None,
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
Rc::new(RefCell::new(MockComputeMeter::default())),
|
||||
&mut ExecuteDetailsTimings::default(),
|
||||
Arc::new(Accounts::default_for_tests()),
|
||||
&Ancestors::default(),
|
||||
Hash::default(),
|
||||
FeeCalculator::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
InstructionError::Custom(0xbabb1e)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ fn test_program_native_noop() {
|
||||
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_native_program("solana_noop_program", &program_id, false);
|
||||
bank.add_builtin_account("solana_noop_program", &program_id, false);
|
||||
|
||||
// Call user program
|
||||
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
|
||||
|
Reference in New Issue
Block a user