* 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) | ||||
|     } | ||||
|  | ||||
|     // 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 | ||||
|     fn update_rewards( | ||||
|         &mut self, | ||||
| @@ -1346,9 +1372,9 @@ impl Bank { | ||||
|         } | ||||
|         // if I'm the first Bank in an epoch, count, claim, disburse rewards from Inflation | ||||
|  | ||||
|         // calculated as: prev_slot / (slots / year) | ||||
|         let slot_in_year = | ||||
|             (self.epoch_schedule.get_last_slot_in_epoch(prev_epoch)) as f64 / self.slots_per_year; | ||||
|         // calculated as: num_slots / (slots / year) | ||||
|         let num_slots = self.get_inflation_num_slots(); | ||||
|         let slot_in_year = num_slots as f64 / self.slots_per_year; | ||||
|  | ||||
|         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 | ||||
|         } | ||||
|  | ||||
|         if new_feature_activations.contains(&feature_set::inflation_kill_switch::id()) { | ||||
|             *self.inflation.write().unwrap() = Inflation::new_disabled(); | ||||
|             self.fee_rate_governor.burn_percent = 100; // 100% fee burn | ||||
|             self.rent_collector.rent.burn_percent = 100; // 100% rent burn | ||||
|         if new_feature_activations.contains(&feature_set::full_inflation::id()) { | ||||
|             *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 | ||||
|         } | ||||
|  | ||||
|         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)); | ||||
|     } | ||||
|  | ||||
|     #[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 { | ||||
|     solana_sdk::declare_id!("GaBtBJvmS4Arjj5W1NmFcyvPjsHN38UGYDq2MDwbs9Qu"); | ||||
|     solana_sdk::declare_id!("4RWNif6C2WCNiKVW7otP4G7dkmkHGyKQWRpuZ1pxKU5m"); | ||||
| } | ||||
|  | ||||
| pub mod inflation_kill_switch { | ||||
|     solana_sdk::declare_id!("SECCKV5UVUsr8sTVSVAzULjdm87r7mLPaqH2FGZjevR"); | ||||
| pub mod full_inflation { | ||||
|     solana_sdk::declare_id!("DT4n6ABDqs6w4bnfwrXT9rsprcPf6cdDga1egctaPkLC"); | ||||
| } | ||||
|  | ||||
| pub mod spl_token_v2_multisig_fix { | ||||
| @@ -97,7 +97,7 @@ lazy_static! { | ||||
|         (secp256k1_program_enabled::id(), "secp256k1 program"), | ||||
|         (consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"), | ||||
|         (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"), | ||||
|         (bpf_loader2_program::id(), "bpf_loader2 program"), | ||||
|         (bpf_compute_budget_balancing::id(), "compute budget balancing"), | ||||
|   | ||||
| @@ -69,6 +69,17 @@ impl 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 | ||||
|     pub fn total(&self, year: f64) -> f64 { | ||||
|         assert!(year >= 0.0); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user