From 8dfe0affd4e7a9221149533cbf5ab160ee922500 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 30 Apr 2020 22:04:08 -0700 Subject: [PATCH] Add incinerator sysvar (#9815) --- runtime/src/accounts.rs | 4 ++-- runtime/src/bank.rs | 44 +++++++++++++++++++++++++++++++++-- runtime/src/rent_collector.rs | 10 +++++--- sdk/macro/src/lib.rs | 8 +++++-- sdk/src/incinerator.rs | 4 ++++ sdk/src/lib.rs | 1 + 6 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 sdk/src/incinerator.rs diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index e693c3d9c..860d85d8b 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -162,7 +162,7 @@ impl Accounts { let (account, rent) = AccountsDB::load(storage, ancestors, accounts_index, key) .and_then(|(mut account, _)| { if message.is_writable(i) && !account.executable { - let rent_due = rent_collector.update(&mut account); + let rent_due = rent_collector.update(&key, &mut account); Some((account, rent_due)) } else { Some((account, 0)) @@ -664,7 +664,7 @@ impl Accounts { if message.is_writable(i) { if account.rent_epoch == 0 { account.rent_epoch = rent_collector.epoch; - acc.2 += rent_collector.update(account); + acc.2 += rent_collector.update(&key, account); } accounts.push((key, &*account)); } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 895e9437c..407a9047e 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -42,6 +42,7 @@ use solana_sdk::{ genesis_config::GenesisConfig, hard_forks::HardForks, hash::{extend_and_hash, hashv, Hash}, + incinerator, inflation::Inflation, native_loader, nonce, pubkey::Pubkey, @@ -795,6 +796,7 @@ impl Bank { self.collect_fees(); self.distribute_rent(); self.update_slot_history(); + self.run_incinerator(); // freeze is a one-way trip, idempotent *hash = self.hash_internal_state(); @@ -1639,6 +1641,14 @@ impl Bank { .fetch_add(collected_rent, Ordering::Relaxed); } + fn run_incinerator(&self) { + if let Some((account, _)) = self.get_account_modified_since_parent(&incinerator::id()) { + self.capitalization + .fetch_sub(account.lamports, Ordering::Relaxed); + self.store_account(&incinerator::id(), &Account::default()); + } + } + /// Process a batch of transactions. #[must_use] pub fn load_execute_and_commit_transactions( @@ -1751,8 +1761,10 @@ impl Bank { pub fn deposit(&self, pubkey: &Pubkey, lamports: u64) { let mut account = self.get_account(pubkey).unwrap_or_default(); - self.collected_rent - .fetch_add(self.rent_collector.update(&mut account), Ordering::Relaxed); + self.collected_rent.fetch_add( + self.rent_collector.update(pubkey, &mut account), + Ordering::Relaxed, + ); account.lamports += lamports; self.store_account(pubkey, &account); } @@ -5843,4 +5855,32 @@ mod tests { info!("account: {:?}", account); assert!(account.executable); } + + #[test] + fn test_incinerator() { + let (genesis_config, mint_keypair) = create_genesis_config(1_000_000_000_000); + let bank0 = Arc::new(Bank::new(&genesis_config)); + + // Move to the first normal slot so normal rent behaviour applies + let bank = Bank::new_from_parent( + &bank0, + &Pubkey::default(), + genesis_config.epoch_schedule.first_normal_slot, + ); + let pre_capitalization = bank.capitalization(); + + // Burn a non-rent exempt amount + let burn_amount = bank.get_minimum_balance_for_rent_exemption(0) - 1; + + assert_eq!(bank.get_balance(&incinerator::id()), 0); + bank.transfer(burn_amount, &mint_keypair, &incinerator::id()) + .unwrap(); + assert_eq!(bank.get_balance(&incinerator::id()), burn_amount); + bank.freeze(); + assert_eq!(bank.get_balance(&incinerator::id()), 0); + + // Ensure that no rent was collected, and the entire burn amount was removed from bank + // capitalization + assert_eq!(bank.capitalization(), pre_capitalization - burn_amount); + } } diff --git a/runtime/src/rent_collector.rs b/runtime/src/rent_collector.rs index 525d7d3b5..6d95a4579 100644 --- a/runtime/src/rent_collector.rs +++ b/runtime/src/rent_collector.rs @@ -1,6 +1,7 @@ //! calculate and collect rent from Accounts use solana_sdk::{ - account::Account, clock::Epoch, epoch_schedule::EpochSchedule, rent::Rent, sysvar, + account::Account, clock::Epoch, epoch_schedule::EpochSchedule, incinerator, pubkey::Pubkey, + rent::Rent, sysvar, }; #[derive(Default, Serialize, Deserialize, Clone)] @@ -35,8 +36,11 @@ impl RentCollector { // updates this account's lamports and status and returns // the account rent collected, if any // - pub fn update(&self, account: &mut Account) -> u64 { - if account.executable || account.rent_epoch > self.epoch || sysvar::check_id(&account.owner) + pub fn update(&self, address: &Pubkey, account: &mut Account) -> u64 { + if account.executable + || account.rent_epoch > self.epoch + || sysvar::check_id(&account.owner) + || *address == incinerator::id() { 0 } else { diff --git a/sdk/macro/src/lib.rs b/sdk/macro/src/lib.rs index 2f3075174..90736556a 100644 --- a/sdk/macro/src/lib.rs +++ b/sdk/macro/src/lib.rs @@ -21,8 +21,12 @@ impl Parse for Id { let id_vec = bs58::decode(id_literal.value()) .into_vec() .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 id"))?; - let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])) - .map_err(|_| syn::Error::new_spanned(&id_literal, "id is not 32 bytes long"))?; + let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| { + syn::Error::new_spanned( + &id_literal, + format!("id is not 32 bytes long: len={}", id_vec.len()), + ) + })?; let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site())); quote! { ::solana_sdk::pubkey::Pubkey::new_from_array( diff --git a/sdk/src/incinerator.rs b/sdk/src/incinerator.rs new file mode 100644 index 000000000..c8f8d6b64 --- /dev/null +++ b/sdk/src/incinerator.rs @@ -0,0 +1,4 @@ +//! Lamports credited to this address will be removed from the total supply (burned) at the end of +//! the current block. + +crate::declare_id!("1nc1nerator11111111111111111111111111111111"); diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index a62a57ae6..1d1a19579 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -10,6 +10,7 @@ pub mod entrypoint_native; pub mod epoch_schedule; pub mod fee_calculator; pub mod hash; +pub mod incinerator; pub mod inflation; pub mod instruction; pub mod loader_instruction;