This repository was archived by the owner on Jan 22, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Allow feature builtins to overwrite existing builtins #13403
Merged
mergify
merged 4 commits into
solana-labs:master
from
CriesofCarrots:feature-builtins-overwrite
Nov 5, 2020
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ use crate::{ | |
| accounts_db::{ErrorCounters, SnapshotStorages}, | ||
| accounts_index::Ancestors, | ||
| blockhash_queue::BlockhashQueue, | ||
| builtins, | ||
| builtins::{self, ActivationType}, | ||
| epoch_stakes::{EpochStakes, NodeVoteAccounts}, | ||
| instruction_recorder::InstructionRecorder, | ||
| log_collector::LogCollector, | ||
|
|
@@ -215,7 +215,7 @@ pub struct Builtins { | |
| pub genesis_builtins: Vec<Builtin>, | ||
|
|
||
| /// Builtin programs activated dynamically by feature | ||
| pub feature_builtins: Vec<(Builtin, Pubkey)>, | ||
| pub feature_builtins: Vec<(Builtin, Pubkey, ActivationType)>, | ||
| } | ||
|
|
||
| const MAX_CACHED_EXECUTORS: usize = 100; // 10 MB assuming programs are around 100k | ||
|
|
@@ -676,7 +676,7 @@ pub struct Bank { | |
| bpf_compute_budget: Option<BpfComputeBudget>, | ||
|
|
||
| /// Builtin programs activated dynamically by feature | ||
| feature_builtins: Arc<Vec<(Builtin, Pubkey)>>, | ||
| feature_builtins: Arc<Vec<(Builtin, Pubkey, ActivationType)>>, | ||
|
|
||
| /// Last time when the cluster info vote listener has synced with this bank | ||
| pub last_vote_sync: AtomicU64, | ||
|
|
@@ -1715,16 +1715,15 @@ 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); | ||
| self.add_native_program(name, program_id, false); | ||
| } | ||
| } | ||
|
|
||
| pub fn add_native_program(&self, name: &str, program_id: &Pubkey) { | ||
| let mut already_genuine_program_exists = false; | ||
| if let Some(mut account) = self.get_account(&program_id) { | ||
| already_genuine_program_exists = native_loader::check_id(&account.owner); | ||
|
|
||
| if !already_genuine_program_exists { | ||
| // NOTE: must hold idempotent for the same set of arguments | ||
| pub fn add_native_program(&self, name: &str, program_id: &Pubkey, must_replace: bool) { | ||
| let mut existing_genuine_program = self.get_account(&program_id); | ||
| if let Some(account) = &mut existing_genuine_program { | ||
| if !native_loader::check_id(&account.owner) { | ||
| // malicious account is pre-occupying at program_id | ||
| // forcibly burn and purge it | ||
|
|
||
|
|
@@ -1737,19 +1736,54 @@ impl Bank { | |
| } | ||
| } | ||
|
|
||
| if !already_genuine_program_exists { | ||
| assert!( | ||
| !self.is_frozen(), | ||
| "Can't change frozen bank by adding not-existing new native program ({}, {}). \ | ||
| Maybe, inconsistent program activation is detected on snapshot restore?", | ||
| name, | ||
| program_id | ||
| ); | ||
| if must_replace { | ||
| // updating native program | ||
|
|
||
| // Add a bogus executable native account, which will be loaded and ignored. | ||
| let account = native_loader::create_loadable_account(name); | ||
| self.store_account(&program_id, &account); | ||
| match existing_genuine_program { | ||
| None => panic!( | ||
| "There is no account to replace with native program ({}, {}).", | ||
| name, program_id | ||
| ), | ||
| Some(account) => { | ||
| if *name == String::from_utf8_lossy(&account.data) { | ||
| // nop; it seems that already AccountsDB is updated. | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| assert!( | ||
| !self.is_frozen(), | ||
| "Can't change frozen bank by adding not-existing new native program ({}, {}). \ | ||
| Maybe, inconsistent program activation is detected on snapshot restore?", | ||
| name, | ||
| program_id | ||
| ); | ||
|
|
||
| // Add a bogus executable native account, which will be loaded and ignored. | ||
| let account = native_loader::create_loadable_account(name); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, when replacing, we must consider the balance of existing program account otherwise we would cause capitalization mismatch if the replaced native program isn't 1 lamports. Fixed and test added at #13884. |
||
| self.store_account(&program_id, &account); | ||
|
|
||
| debug!("Added native program {} under {:?}", name, program_id); | ||
| } | ||
|
|
||
|
|
@@ -3886,8 +3920,21 @@ impl Bank { | |
| program_id: Pubkey, | ||
| process_instruction_with_context: ProcessInstructionWithContext, | ||
| ) { | ||
| debug!("Added program {} under {:?}", name, program_id); | ||
| self.add_native_program(name, &program_id); | ||
| debug!("Adding program {} under {:?}", name, program_id); | ||
| self.add_native_program(name, &program_id, false); | ||
| self.message_processor | ||
| .add_program(program_id, process_instruction_with_context); | ||
| } | ||
|
|
||
| /// Replace a builtin instruction processor if it already exists | ||
| pub fn replace_builtin( | ||
| &mut self, | ||
| name: &str, | ||
| program_id: Pubkey, | ||
| process_instruction_with_context: ProcessInstructionWithContext, | ||
| ) { | ||
| debug!("Replacing program {} under {:?}", name, program_id); | ||
| self.add_native_program(name, &program_id, true); | ||
| self.message_processor | ||
| .add_program(program_id, process_instruction_with_context); | ||
| } | ||
|
|
@@ -4021,15 +4068,22 @@ impl Bank { | |
| new_feature_activations: &HashSet<Pubkey>, | ||
| ) { | ||
| let feature_builtins = self.feature_builtins.clone(); | ||
| for (builtin, feature) in feature_builtins.iter() { | ||
| for (builtin, feature, activation_type) in feature_builtins.iter() { | ||
| 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( | ||
| &builtin.name, | ||
| builtin.id, | ||
| builtin.process_instruction_with_context, | ||
| ); | ||
| match activation_type { | ||
| ActivationType::NewProgram => self.add_builtin( | ||
| &builtin.name, | ||
| builtin.id, | ||
| builtin.process_instruction_with_context, | ||
| ), | ||
| ActivationType::NewVersion => self.replace_builtin( | ||
| &builtin.name, | ||
| builtin.id, | ||
| builtin.process_instruction_with_context, | ||
| ), | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -9232,6 +9286,16 @@ mod tests { | |
| .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, | ||
| mock_ix_processor, | ||
| ); | ||
| assert_eq!( | ||
| bank.get_account_modified_slot(&program_id).unwrap().1, | ||
| bank.slot() | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -9285,14 +9349,35 @@ mod tests { | |
|
|
||
| Arc::get_mut(&mut bank) | ||
| .unwrap() | ||
| .add_native_program("mock_program", &program_id); | ||
| .add_native_program("mock_program", &program_id, false); | ||
| 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_native_program("mock_program", &program_id); | ||
| .add_native_program("mock_program", &program_id, false); | ||
| assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); | ||
|
|
||
| let mut bank = Arc::new(new_from_parent(&bank)); | ||
| // When replacing native_program, name must change to disambiguate from repeated | ||
| // invocations. | ||
|
Comment on lines
+9362
to
+9363
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should put a comment about this in builtins.rs, since it's critical to know when constructing the name
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll sneak that into #13394 |
||
| Arc::get_mut(&mut bank) | ||
| .unwrap() | ||
| .add_native_program("mock_program v2", &program_id, true); | ||
| assert_eq!( | ||
| bank.get_account_modified_slot(&program_id).unwrap().1, | ||
| bank.slot() | ||
| ); | ||
|
|
||
| let mut bank = Arc::new(new_from_parent(&bank)); | ||
| Arc::get_mut(&mut bank) | ||
| .unwrap() | ||
| .add_native_program("mock_program v2", &program_id, true); | ||
| // replacing with same name shouldn't update account | ||
| assert_eq!( | ||
| bank.get_account_modified_slot(&program_id).unwrap().1, | ||
| bank.parent_slot() | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -9308,16 +9393,35 @@ mod tests { | |
| let slot = 123; | ||
| let program_id = Pubkey::from_str("CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre").unwrap(); | ||
|
|
||
| let mut bank = Arc::new(Bank::new_from_parent( | ||
| let bank = Bank::new_from_parent( | ||
| &Arc::new(Bank::new(&genesis_config)), | ||
| &Pubkey::default(), | ||
| slot, | ||
| )); | ||
| ); | ||
| bank.freeze(); | ||
|
|
||
| Arc::get_mut(&mut bank) | ||
| .unwrap() | ||
| .add_native_program("mock_program", &program_id); | ||
| bank.add_native_program("mock_program", &program_id, false); | ||
| } | ||
|
|
||
| #[test] | ||
| #[should_panic( | ||
| expected = "There is no account to replace with native program (mock_program, \ | ||
| CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre)." | ||
| )] | ||
| fn test_add_native_program_replace_none() { | ||
| 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(&genesis_config)), | ||
| &Pubkey::default(), | ||
| slot, | ||
| ); | ||
|
|
||
| bank.add_native_program("mock_program", &program_id, true); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well this logic changes introduced a regression where we could be tricked into skipping to create native program because
existing_genuine_programcan be set to a bad account and not reset and we enters thismatcharm returning from thisfn: https://github.com/solana-labs/solana/pull/13403/files#diff-ed47b4a0198313377e091bb3957bbbc63d937805426d1b2b6de39d0a50d32a0cR1760Fixed and test is imrproved at #13884.