* Add new inflation feature-ids, and full_inflation default values
* Compute inflation start from full_inflation activation
* Include pico_inflation in inflation start computation
* Add full-inflation constructor
* Align inflation taper with rewards accrual start and catch overflow edge case
(cherry picked from commit c75d97e3f2)
Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
			
			
This commit is contained in:
		| @@ -1335,6 +1335,32 @@ impl Bank { | |||||||
|         (examined_count, rewritten_count) |         (examined_count, rewritten_count) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Calculates the starting-slot for inflation from the activation slot. | ||||||
|  |     // This method assumes that `pico_inflation` will be enabled before `full_inflation`, giving | ||||||
|  |     // precedence to the latter. However, since `pico_inflation` is fixed-rate Inflation, should | ||||||
|  |     // `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(|| { | ||||||
|  |                 self.feature_set | ||||||
|  |                     .activated_slot(&feature_set::pico_inflation::id()) | ||||||
|  |                     .unwrap_or(0) | ||||||
|  |             }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn get_inflation_num_slots(&self) -> u64 { | ||||||
|  |         let inflation_activation_slot = self.get_inflation_start_slot(); | ||||||
|  |         // Normalize inflation_start to align with the start of rewards accrual. | ||||||
|  |         let inflation_start_slot = self.epoch_schedule.get_first_slot_in_epoch( | ||||||
|  |             self.epoch_schedule | ||||||
|  |                 .get_epoch(inflation_activation_slot) | ||||||
|  |                 .saturating_sub(1), | ||||||
|  |         ); | ||||||
|  |         self.epoch_schedule.get_first_slot_in_epoch(self.epoch()) - inflation_start_slot | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // update rewards based on the previous epoch |     // update rewards based on the previous epoch | ||||||
|     fn update_rewards( |     fn update_rewards( | ||||||
|         &mut self, |         &mut self, | ||||||
| @@ -1346,9 +1372,9 @@ impl Bank { | |||||||
|         } |         } | ||||||
|         // if I'm the first Bank in an epoch, count, claim, disburse rewards from Inflation |         // if I'm the first Bank in an epoch, count, claim, disburse rewards from Inflation | ||||||
|  |  | ||||||
|         // calculated as: prev_slot / (slots / year) |         // calculated as: num_slots / (slots / year) | ||||||
|         let slot_in_year = |         let num_slots = self.get_inflation_num_slots(); | ||||||
|             (self.epoch_schedule.get_last_slot_in_epoch(prev_epoch)) as f64 / self.slots_per_year; |         let slot_in_year = num_slots as f64 / self.slots_per_year; | ||||||
|  |  | ||||||
|         let epoch_duration_in_years = self.epoch_duration_in_years(prev_epoch); |         let epoch_duration_in_years = self.epoch_duration_in_years(prev_epoch); | ||||||
|  |  | ||||||
| @@ -4142,10 +4168,10 @@ impl Bank { | |||||||
|             self.rent_collector.rent.burn_percent = 50; // 50% rent burn |             self.rent_collector.rent.burn_percent = 50; // 50% rent burn | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if new_feature_activations.contains(&feature_set::inflation_kill_switch::id()) { |         if new_feature_activations.contains(&feature_set::full_inflation::id()) { | ||||||
|             *self.inflation.write().unwrap() = Inflation::new_disabled(); |             *self.inflation.write().unwrap() = Inflation::full(); | ||||||
|             self.fee_rate_governor.burn_percent = 100; // 100% fee burn |             self.fee_rate_governor.burn_percent = 50; // 50% fee burn | ||||||
|             self.rent_collector.rent.burn_percent = 100; // 100% rent burn |             self.rent_collector.rent.burn_percent = 50; // 50% rent burn | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if new_feature_activations.contains(&feature_set::spl_token_v2_multisig_fix::id()) { |         if new_feature_activations.contains(&feature_set::spl_token_v2_multisig_fix::id()) { | ||||||
| @@ -10700,4 +10726,140 @@ pub(crate) mod tests { | |||||||
|  |  | ||||||
|         assert_eq!(bank.rewrite_stakes(), (1, 1)); |         assert_eq!(bank.rewrite_stakes(), (1, 1)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_get_inflation_start_slot() { | ||||||
|  |         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::id()) | ||||||
|  |             .unwrap(); | ||||||
|  |         let bank = Bank::new(&genesis_config); | ||||||
|  |  | ||||||
|  |         // Advance to slot 1 | ||||||
|  |         let mut bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         assert_eq!(bank.get_inflation_start_slot(), 0); | ||||||
|  |  | ||||||
|  |         // Request `full_inflation` activation | ||||||
|  |         let pico_inflation_activation_slot = 1; | ||||||
|  |         bank.store_account( | ||||||
|  |             &feature_set::pico_inflation::id(), | ||||||
|  |             &feature::create_account( | ||||||
|  |                 &Feature { | ||||||
|  |                     activated_at: Some(pico_inflation_activation_slot), | ||||||
|  |                 }, | ||||||
|  |                 42, | ||||||
|  |             ), | ||||||
|  |         ); | ||||||
|  |         bank.compute_active_feature_set(true); | ||||||
|  |         assert_eq!( | ||||||
|  |             bank.get_inflation_start_slot(), | ||||||
|  |             pico_inflation_activation_slot | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // Advance to slot 2 | ||||||
|  |         bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |  | ||||||
|  |         // Request `full_inflation` activation, which takes priority over pico_inflation | ||||||
|  |         let full_inflation_activation_slot = 2; | ||||||
|  |         bank.store_account( | ||||||
|  |             &feature_set::full_inflation::id(), | ||||||
|  |             &feature::create_account( | ||||||
|  |                 &Feature { | ||||||
|  |                     activated_at: Some(full_inflation_activation_slot), | ||||||
|  |                 }, | ||||||
|  |                 42, | ||||||
|  |             ), | ||||||
|  |         ); | ||||||
|  |         bank.compute_active_feature_set(true); | ||||||
|  |         assert_eq!( | ||||||
|  |             bank.get_inflation_start_slot(), | ||||||
|  |             full_inflation_activation_slot | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_get_inflation_num_slots_with_activations() { | ||||||
|  |         let GenesisConfigInfo { | ||||||
|  |             mut genesis_config, .. | ||||||
|  |         } = create_genesis_config_with_leader(42, &solana_sdk::pubkey::new_rand(), 42); | ||||||
|  |         let slots_per_epoch = 32; | ||||||
|  |         genesis_config.epoch_schedule = EpochSchedule::new(slots_per_epoch); | ||||||
|  |         genesis_config | ||||||
|  |             .accounts | ||||||
|  |             .remove(&feature_set::pico_inflation::id()) | ||||||
|  |             .unwrap(); | ||||||
|  |         genesis_config | ||||||
|  |             .accounts | ||||||
|  |             .remove(&feature_set::full_inflation::id()) | ||||||
|  |             .unwrap(); | ||||||
|  |         let mut bank = Bank::new(&genesis_config); | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 0); | ||||||
|  |         for _ in 0..2 * slots_per_epoch { | ||||||
|  |             bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         } | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 2 * slots_per_epoch); | ||||||
|  |  | ||||||
|  |         // Activate pico_inflation | ||||||
|  |         let pico_inflation_activation_slot = bank.slot(); | ||||||
|  |         bank.store_account( | ||||||
|  |             &feature_set::pico_inflation::id(), | ||||||
|  |             &feature::create_account( | ||||||
|  |                 &Feature { | ||||||
|  |                     activated_at: Some(pico_inflation_activation_slot), | ||||||
|  |                 }, | ||||||
|  |                 42, | ||||||
|  |             ), | ||||||
|  |         ); | ||||||
|  |         bank.compute_active_feature_set(true); | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), slots_per_epoch); | ||||||
|  |         for _ in 0..slots_per_epoch { | ||||||
|  |             bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         } | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 2 * slots_per_epoch); | ||||||
|  |  | ||||||
|  |         // Activate full_inflation | ||||||
|  |         let full_inflation_activation_slot = bank.slot(); | ||||||
|  |         bank.store_account( | ||||||
|  |             &feature_set::full_inflation::id(), | ||||||
|  |             &feature::create_account( | ||||||
|  |                 &Feature { | ||||||
|  |                     activated_at: Some(full_inflation_activation_slot), | ||||||
|  |                 }, | ||||||
|  |                 42, | ||||||
|  |             ), | ||||||
|  |         ); | ||||||
|  |         bank.compute_active_feature_set(true); | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), slots_per_epoch); | ||||||
|  |         for _ in 0..slots_per_epoch { | ||||||
|  |             bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         } | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 2 * slots_per_epoch); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_get_inflation_num_slots_already_activated() { | ||||||
|  |         let GenesisConfigInfo { | ||||||
|  |             mut genesis_config, .. | ||||||
|  |         } = create_genesis_config_with_leader(42, &solana_sdk::pubkey::new_rand(), 42); | ||||||
|  |         let slots_per_epoch = 32; | ||||||
|  |         genesis_config.epoch_schedule = EpochSchedule::new(slots_per_epoch); | ||||||
|  |         let mut bank = Bank::new(&genesis_config); | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 0); | ||||||
|  |         for _ in 0..slots_per_epoch { | ||||||
|  |             bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         } | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), slots_per_epoch); | ||||||
|  |         for _ in 0..slots_per_epoch { | ||||||
|  |             bank = new_from_parent(&Arc::new(bank)); | ||||||
|  |         } | ||||||
|  |         assert_eq!(bank.get_inflation_num_slots(), 2 * slots_per_epoch); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,11 +19,11 @@ pub mod consistent_recent_blockhashes_sysvar { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub mod pico_inflation { | pub mod pico_inflation { | ||||||
|     solana_sdk::declare_id!("GaBtBJvmS4Arjj5W1NmFcyvPjsHN38UGYDq2MDwbs9Qu"); |     solana_sdk::declare_id!("4RWNif6C2WCNiKVW7otP4G7dkmkHGyKQWRpuZ1pxKU5m"); | ||||||
| } | } | ||||||
|  |  | ||||||
| pub mod inflation_kill_switch { | pub mod full_inflation { | ||||||
|     solana_sdk::declare_id!("SECCKV5UVUsr8sTVSVAzULjdm87r7mLPaqH2FGZjevR"); |     solana_sdk::declare_id!("DT4n6ABDqs6w4bnfwrXT9rsprcPf6cdDga1egctaPkLC"); | ||||||
| } | } | ||||||
|  |  | ||||||
| pub mod spl_token_v2_multisig_fix { | pub mod spl_token_v2_multisig_fix { | ||||||
| @@ -97,7 +97,7 @@ lazy_static! { | |||||||
|         (secp256k1_program_enabled::id(), "secp256k1 program"), |         (secp256k1_program_enabled::id(), "secp256k1 program"), | ||||||
|         (consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"), |         (consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"), | ||||||
|         (pico_inflation::id(), "pico-inflation"), |         (pico_inflation::id(), "pico-inflation"), | ||||||
|         (inflation_kill_switch::id(), "inflation kill switch"), |         (full_inflation::id(), "full-inflation"), | ||||||
|         (spl_token_v2_multisig_fix::id(), "spl-token multisig fix"), |         (spl_token_v2_multisig_fix::id(), "spl-token multisig fix"), | ||||||
|         (bpf_loader2_program::id(), "bpf_loader2 program"), |         (bpf_loader2_program::id(), "bpf_loader2 program"), | ||||||
|         (bpf_compute_budget_balancing::id(), "compute budget balancing"), |         (bpf_compute_budget_balancing::id(), "compute budget balancing"), | ||||||
|   | |||||||
| @@ -69,6 +69,17 @@ impl Inflation { | |||||||
|         Self::new_fixed(0.0001) // 0.01% inflation |         Self::new_fixed(0.0001) // 0.01% inflation | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub fn full() -> Self { | ||||||
|  |         Self { | ||||||
|  |             initial: DEFAULT_INITIAL, | ||||||
|  |             terminal: DEFAULT_TERMINAL, | ||||||
|  |             taper: DEFAULT_TAPER, | ||||||
|  |             foundation: 0.0, | ||||||
|  |             foundation_term: 0.0, | ||||||
|  |             __unused: 0.0, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// inflation rate at year |     /// inflation rate at year | ||||||
|     pub fn total(&self, year: f64) -> f64 { |     pub fn total(&self, year: f64) -> f64 { | ||||||
|         assert!(year >= 0.0); |         assert!(year >= 0.0); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user