Add generalized voting process to enable full inflation (#14731)
(cherry picked from commit 072e5e54d8)
Co-authored-by: Michael Vines <mvines@gmail.com>
			
			
This commit is contained in:
		| @@ -1540,9 +1540,14 @@ impl Bank { | ||||
|     // `pico_inflation` be enabled 2nd, the incorrect start slot provided here should have no | ||||
|     // effect on the inflation calculation. | ||||
|     fn get_inflation_start_slot(&self) -> Slot { | ||||
|         self.feature_set | ||||
|             .activated_slot(&feature_set::full_inflation::id()) | ||||
|             .unwrap_or_else(|| { | ||||
|         let mut slots = self | ||||
|             .feature_set | ||||
|             .full_inflation_features_enabled() | ||||
|             .iter() | ||||
|             .filter_map(|id| self.feature_set.activated_slot(&id)) | ||||
|             .collect::<Vec<_>>(); | ||||
|         slots.sort_unstable(); | ||||
|         slots.get(0).cloned().unwrap_or_else(|| { | ||||
|             self.feature_set | ||||
|                 .activated_slot(&feature_set::pico_inflation::id()) | ||||
|                 .unwrap_or(0) | ||||
| @@ -4571,7 +4576,8 @@ impl Bank { | ||||
|             self.rent_collector.rent.burn_percent = 50; // 50% rent burn | ||||
|         } | ||||
|  | ||||
|         if new_feature_activations.contains(&feature_set::full_inflation::id()) { | ||||
|         if !new_feature_activations.is_disjoint(&self.feature_set.full_inflation_features_enabled()) | ||||
|         { | ||||
|             *self.inflation.write().unwrap() = Inflation::full(); | ||||
|             self.fee_rate_governor.burn_percent = 50; // 50% fee burn | ||||
|             self.rent_collector.rent.burn_percent = 50; // 50% rent burn | ||||
| @@ -11729,7 +11735,7 @@ pub(crate) mod tests { | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_inflation_start_slot() { | ||||
|     fn test_get_inflation_start_slot_devnet_testnet() { | ||||
|         let GenesisConfigInfo { | ||||
|             mut genesis_config, .. | ||||
|         } = create_genesis_config_with_leader(42, &solana_sdk::pubkey::new_rand(), 42); | ||||
| @@ -11739,51 +11745,156 @@ pub(crate) mod tests { | ||||
|             .unwrap(); | ||||
|         genesis_config | ||||
|             .accounts | ||||
|             .remove(&feature_set::full_inflation::id()) | ||||
|             .remove(&feature_set::full_inflation::devnet_and_testnet::id()) | ||||
|             .unwrap(); | ||||
|         for pair in feature_set::FULL_INFLATION_FEATURE_PAIRS.iter() { | ||||
|             genesis_config.accounts.remove(&pair.vote_id).unwrap(); | ||||
|             genesis_config.accounts.remove(&pair.enable_id).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let bank = Bank::new(&genesis_config); | ||||
|  | ||||
|         // Advance to slot 1 | ||||
|         // Advance slot | ||||
|         let mut bank = new_from_parent(&Arc::new(bank)); | ||||
|         bank = new_from_parent(&Arc::new(bank)); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 0); | ||||
|         assert_eq!(bank.slot(), 2); | ||||
|  | ||||
|         // Request `full_inflation` activation | ||||
|         let pico_inflation_activation_slot = 1; | ||||
|         // Request `pico_inflation` activation | ||||
|         bank.store_account( | ||||
|             &feature_set::pico_inflation::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(pico_inflation_activation_slot), | ||||
|                     activated_at: Some(1), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!( | ||||
|             bank.get_inflation_start_slot(), | ||||
|             pico_inflation_activation_slot | ||||
|         ); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 1); | ||||
|  | ||||
|         // Advance to slot 2 | ||||
|         // Advance slot | ||||
|         bank = new_from_parent(&Arc::new(bank)); | ||||
|         assert_eq!(bank.slot(), 3); | ||||
|  | ||||
|         // Request `full_inflation` activation, which takes priority over pico_inflation | ||||
|         let full_inflation_activation_slot = 2; | ||||
|         // Request `full_inflation::devnet_and_testnet` activation, which takes priority over pico_inflation | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::id(), | ||||
|             &feature_set::full_inflation::devnet_and_testnet::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(full_inflation_activation_slot), | ||||
|                     activated_at: Some(2), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!( | ||||
|             bank.get_inflation_start_slot(), | ||||
|             full_inflation_activation_slot | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 2); | ||||
|  | ||||
|         // Request `full_inflation::candidate_example` activation, which should have no effect on `get_inflation_start_slot` | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::candidate_example::vote::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(3), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::candidate_example::enable::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(3), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 2); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_inflation_start_slot_mainnet() { | ||||
|         let GenesisConfigInfo { | ||||
|             mut genesis_config, .. | ||||
|         } = create_genesis_config_with_leader(42, &solana_sdk::pubkey::new_rand(), 42); | ||||
|         genesis_config | ||||
|             .accounts | ||||
|             .remove(&feature_set::pico_inflation::id()) | ||||
|             .unwrap(); | ||||
|         genesis_config | ||||
|             .accounts | ||||
|             .remove(&feature_set::full_inflation::devnet_and_testnet::id()) | ||||
|             .unwrap(); | ||||
|         for pair in feature_set::FULL_INFLATION_FEATURE_PAIRS.iter() { | ||||
|             genesis_config.accounts.remove(&pair.vote_id).unwrap(); | ||||
|             genesis_config.accounts.remove(&pair.enable_id).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let bank = Bank::new(&genesis_config); | ||||
|  | ||||
|         // Advance slot | ||||
|         let mut bank = new_from_parent(&Arc::new(bank)); | ||||
|         bank = new_from_parent(&Arc::new(bank)); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 0); | ||||
|         assert_eq!(bank.slot(), 2); | ||||
|  | ||||
|         // Request `pico_inflation` activation | ||||
|         bank.store_account( | ||||
|             &feature_set::pico_inflation::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(1), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 1); | ||||
|  | ||||
|         // Advance slot | ||||
|         bank = new_from_parent(&Arc::new(bank)); | ||||
|         assert_eq!(bank.slot(), 3); | ||||
|  | ||||
|         // Request `full_inflation::candidate_example` activation, which takes priority over pico_inflation | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::candidate_example::vote::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(2), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::candidate_example::enable::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(2), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 2); | ||||
|  | ||||
|         // Advance slot | ||||
|         bank = new_from_parent(&Arc::new(bank)); | ||||
|         assert_eq!(bank.slot(), 4); | ||||
|  | ||||
|         // Request `full_inflation::devnet_and_testnet` activation, which should have no effect on | ||||
|         // `get_inflation_start_slot` | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::devnet_and_testnet::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(bank.slot()), | ||||
|                 }, | ||||
|                 42, | ||||
|             ), | ||||
|         ); | ||||
|         bank.compute_active_feature_set(true); | ||||
|         assert_eq!(bank.get_inflation_start_slot(), 2); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -11799,8 +11910,13 @@ pub(crate) mod tests { | ||||
|             .unwrap(); | ||||
|         genesis_config | ||||
|             .accounts | ||||
|             .remove(&feature_set::full_inflation::id()) | ||||
|             .remove(&feature_set::full_inflation::devnet_and_testnet::id()) | ||||
|             .unwrap(); | ||||
|         for pair in feature_set::FULL_INFLATION_FEATURE_PAIRS.iter() { | ||||
|             genesis_config.accounts.remove(&pair.vote_id).unwrap(); | ||||
|             genesis_config.accounts.remove(&pair.enable_id).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let mut bank = Bank::new(&genesis_config); | ||||
|         assert_eq!(bank.get_inflation_num_slots(), 0); | ||||
|         for _ in 0..2 * slots_per_epoch { | ||||
| @@ -11826,10 +11942,10 @@ pub(crate) mod tests { | ||||
|         } | ||||
|         assert_eq!(bank.get_inflation_num_slots(), 2 * slots_per_epoch); | ||||
|  | ||||
|         // Activate full_inflation | ||||
|         // Activate full_inflation::devnet_and_testnet | ||||
|         let full_inflation_activation_slot = bank.slot(); | ||||
|         bank.store_account( | ||||
|             &feature_set::full_inflation::id(), | ||||
|             &feature_set::full_inflation::devnet_and_testnet::id(), | ||||
|             &feature::create_account( | ||||
|                 &Feature { | ||||
|                     activated_at: Some(full_inflation_activation_slot), | ||||
|   | ||||
| @@ -27,7 +27,27 @@ pub mod pico_inflation { | ||||
| } | ||||
|  | ||||
| pub mod full_inflation { | ||||
|     pub mod devnet_and_testnet { | ||||
|         solana_sdk::declare_id!("DT4n6ABDqs6w4bnfwrXT9rsprcPf6cdDga1egctaPkLC"); | ||||
|     } | ||||
|  | ||||
|     // `candidate_example` is an example to follow by a candidate that wishes to enable full | ||||
|     // inflation.  There are multiple references to `candidate_example` in this file that need to | ||||
|     // be touched in addition to the following block. | ||||
|     // | ||||
|     // The candidate provides the `enable::id` address and contacts the Solana Foundation to | ||||
|     // receive a `vote::id` address. | ||||
|     // | ||||
|     pub mod candidate_example { | ||||
|         pub mod vote { | ||||
|             // The private key for this address is held by the Solana Foundation | ||||
|             solana_sdk::declare_id!("DummyVoteAddress111111111111111111111111111"); | ||||
|         } | ||||
|         pub mod enable { | ||||
|             // The private key for this address is held by candidate_example | ||||
|             solana_sdk::declare_id!("DummyEnab1eAddress1111111111111111111111111"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub mod spl_token_v2_multisig_fix { | ||||
| @@ -149,8 +169,8 @@ lazy_static! { | ||||
|         (secp256k1_program_enabled::id(), "secp256k1 program"), | ||||
|         (consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"), | ||||
|         (deprecate_rewards_sysvar::id(), "deprecate unused rewards sysvar"), | ||||
|         (pico_inflation::id(), "pico-inflation"), | ||||
|         (full_inflation::id(), "full-inflation"), | ||||
|         (pico_inflation::id(), "pico inflation"), | ||||
|         (full_inflation::devnet_and_testnet::id(), "full inflation on devnet and testnet"), | ||||
|         (spl_token_v2_multisig_fix::id(), "spl-token multisig fix"), | ||||
|         (bpf_loader2_program::id(), "bpf_loader2 program"), | ||||
|         (bpf_compute_budget_balancing::id(), "compute budget balancing"), | ||||
| @@ -179,6 +199,8 @@ lazy_static! { | ||||
|         (warp_timestamp::id(), "warp timestamp to current, adjust bounding to 50% #14532"), | ||||
|         (turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"), | ||||
|         (prevent_upgrade_and_invoke::id(), "Prevent upgrade and invoke in same tx batch"), | ||||
|         (full_inflation::candidate_example::vote::id(), "Community vote allowing candidate_example to enable full inflation"), | ||||
|         (full_inflation::candidate_example::enable::id(), "Full inflation enabled by candidate_example"), | ||||
|         /*************** ADD NEW FEATURES HERE ***************/ | ||||
|     ] | ||||
|     .iter() | ||||
| @@ -197,6 +219,25 @@ lazy_static! { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[derive(Clone, PartialEq, Eq, Hash)] | ||||
| pub struct FullInflationFeaturePair { | ||||
|     pub vote_id: Pubkey, // Feature that grants the candidate the ability to enable full inflation | ||||
|     pub enable_id: Pubkey, // Feature to enable full inflation by the candidate | ||||
| } | ||||
|  | ||||
| lazy_static! { | ||||
|     /// Set of feature pairs that once enabled will trigger full inflation | ||||
|     pub static ref FULL_INFLATION_FEATURE_PAIRS: HashSet<FullInflationFeaturePair> = [ | ||||
|         FullInflationFeaturePair { | ||||
|             vote_id: full_inflation::candidate_example::vote::id(), | ||||
|             enable_id: full_inflation::candidate_example::enable::id(), | ||||
|         }, | ||||
|     ] | ||||
|     .iter() | ||||
|     .cloned() | ||||
|     .collect(); | ||||
| } | ||||
|  | ||||
| /// `FeatureSet` holds the set of currently active/inactive runtime features | ||||
| #[derive(AbiExample, Debug, Clone)] | ||||
| pub struct FeatureSet { | ||||
| @@ -225,6 +266,25 @@ impl FeatureSet { | ||||
|         self.is_active(&cumulative_rent_related_fixes::id()) | ||||
|     } | ||||
|  | ||||
|     /// List of enabled features that trigger full inflation | ||||
|     pub fn full_inflation_features_enabled(&self) -> HashSet<Pubkey> { | ||||
|         let mut hash_set = FULL_INFLATION_FEATURE_PAIRS | ||||
|             .iter() | ||||
|             .filter_map(|pair| { | ||||
|                 if self.is_active(&pair.vote_id) && self.is_active(&pair.enable_id) { | ||||
|                     Some(pair.enable_id) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }) | ||||
|             .collect::<HashSet<_>>(); | ||||
|  | ||||
|         if self.is_active(&full_inflation::devnet_and_testnet::id()) { | ||||
|             hash_set.insert(full_inflation::devnet_and_testnet::id()); | ||||
|         } | ||||
|         hash_set | ||||
|     } | ||||
|  | ||||
|     /// All features enabled, useful for testing | ||||
|     pub fn all_enabled() -> Self { | ||||
|         Self { | ||||
| @@ -233,3 +293,63 @@ impl FeatureSet { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_full_inflation_features_enabled_devnet_and_testnet() { | ||||
|         let mut feature_set = FeatureSet::default(); | ||||
|         assert!(feature_set.full_inflation_features_enabled().is_empty()); | ||||
|         feature_set | ||||
|             .active | ||||
|             .insert(full_inflation::devnet_and_testnet::id(), 42); | ||||
|         assert_eq!( | ||||
|             feature_set.full_inflation_features_enabled(), | ||||
|             [full_inflation::devnet_and_testnet::id()] | ||||
|                 .iter() | ||||
|                 .cloned() | ||||
|                 .collect() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_full_inflation_features_enabled() { | ||||
|         // Normal sequence: vote_id then enable_id | ||||
|         let mut feature_set = FeatureSet::default(); | ||||
|         assert!(feature_set.full_inflation_features_enabled().is_empty()); | ||||
|         feature_set | ||||
|             .active | ||||
|             .insert(full_inflation::candidate_example::vote::id(), 42); | ||||
|         assert!(feature_set.full_inflation_features_enabled().is_empty()); | ||||
|         feature_set | ||||
|             .active | ||||
|             .insert(full_inflation::candidate_example::enable::id(), 42); | ||||
|         assert_eq!( | ||||
|             feature_set.full_inflation_features_enabled(), | ||||
|             [full_inflation::candidate_example::enable::id()] | ||||
|                 .iter() | ||||
|                 .cloned() | ||||
|                 .collect() | ||||
|         ); | ||||
|  | ||||
|         // Backwards sequence: enable_id and then vote_id | ||||
|         let mut feature_set = FeatureSet::default(); | ||||
|         assert!(feature_set.full_inflation_features_enabled().is_empty()); | ||||
|         feature_set | ||||
|             .active | ||||
|             .insert(full_inflation::candidate_example::enable::id(), 42); | ||||
|         assert!(feature_set.full_inflation_features_enabled().is_empty()); | ||||
|         feature_set | ||||
|             .active | ||||
|             .insert(full_inflation::candidate_example::vote::id(), 42); | ||||
|         assert_eq!( | ||||
|             feature_set.full_inflation_features_enabled(), | ||||
|             [full_inflation::candidate_example::enable::id()] | ||||
|                 .iter() | ||||
|                 .cloned() | ||||
|                 .collect() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user