From 38b648c14a95663ade3bc05ffb1e90dfe059e88b Mon Sep 17 00:00:00 2001 From: Joe C Date: Wed, 7 Feb 2024 16:42:41 -0600 Subject: [PATCH] runtime: core_bpf_migration: add migration path --- .../core_bpf_migration/bpf_upgradeable.rs | 1 - .../builtins/core_bpf_migration/builtin.rs | 1 - .../bank/builtins/core_bpf_migration/error.rs | 3 + .../bank/builtins/core_bpf_migration/mod.rs | 88 ++++++++++++++++++- runtime/src/bank/builtins/prototypes.rs | 35 +++++++- 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/runtime/src/bank/builtins/core_bpf_migration/bpf_upgradeable.rs b/runtime/src/bank/builtins/core_bpf_migration/bpf_upgradeable.rs index 6722e89293778f..825d3c5d4fe140 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/bpf_upgradeable.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/bpf_upgradeable.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] // Removed in later commit use { super::error::CoreBpfMigrationError, crate::bank::Bank, diff --git a/runtime/src/bank/builtins/core_bpf_migration/builtin.rs b/runtime/src/bank/builtins/core_bpf_migration/builtin.rs index eb357f1bbf1a7a..5af73b29e6bed3 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/builtin.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/builtin.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] // Removed in later commit use { super::{error::CoreBpfMigrationError, CoreBpfMigration}, crate::bank::Bank, diff --git a/runtime/src/bank/builtins/core_bpf_migration/error.rs b/runtime/src/bank/builtins/core_bpf_migration/error.rs index 5b0c79116938c1..5aed271ab5471e 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/error.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/error.rs @@ -24,6 +24,9 @@ pub enum CoreBpfMigrationError { /// Invalid program data account #[error("Invalid program data account: {0:?}")] InvalidProgramDataAccount(Pubkey), + /// Failed to serialize new program account + #[error("Failed to serialize new program account")] + FailedToSerialize, // Since `core_bpf_migration` does not return `ProgramError` or // `InstructionError`, we have to duplicate `ArithmeticOverflow` here. /// Arithmetic overflow diff --git a/runtime/src/bank/builtins/core_bpf_migration/mod.rs b/runtime/src/bank/builtins/core_bpf_migration/mod.rs index b913fd39b904d1..d357cfeba9f2d3 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/mod.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/mod.rs @@ -1,9 +1,19 @@ -#![allow(dead_code)] // Removed in later commit mod bpf_upgradeable; mod builtin; -mod error; +pub(crate) mod error; -use solana_sdk::pubkey::Pubkey; +use { + crate::bank::Bank, + bpf_upgradeable::BpfUpgradeableConfig, + builtin::BuiltinConfig, + error::CoreBpfMigrationError, + solana_sdk::{ + account::{Account, AccountSharedData}, + bpf_loader_upgradeable::{UpgradeableLoaderState, ID as BPF_LOADER_UPGRADEABLE_ID}, + pubkey::Pubkey, + }, + std::sync::atomic::Ordering::Relaxed, +}; /// Sets up a Core BPF migration for a built-in program. pub enum CoreBpfMigration { @@ -15,6 +25,7 @@ pub enum CoreBpfMigration { pub struct CoreBpfMigrationConfig { pub source_program_id: Pubkey, pub feature_id: Pubkey, + pub datapoint_name: &'static str, } impl std::fmt::Debug for CoreBpfMigrationConfig { @@ -25,3 +36,74 @@ impl std::fmt::Debug for CoreBpfMigrationConfig { builder.finish() } } + +/// Create a new `Account` with a pointer to the target's new data account. +/// +/// Note the pointer is created manually, as well as the owner and +/// executable values. The rest is inherited from the source program +/// account, including the lamports. +fn create_new_target_program_account( + target: &BuiltinConfig, + source: &BpfUpgradeableConfig, +) -> Result { + let state = UpgradeableLoaderState::Program { + programdata_address: target.program_data_address, + }; + let data = bincode::serialize(&state).map_err(|_| CoreBpfMigrationError::FailedToSerialize)?; + let account = Account { + data, + owner: BPF_LOADER_UPGRADEABLE_ID, + executable: true, + ..source.program_account + }; + Ok(AccountSharedData::from(account)) +} + +impl CoreBpfMigrationConfig { + pub(crate) fn migrate_builtin_to_core_bpf( + &self, + bank: &mut Bank, + program_id: &Pubkey, + migration: CoreBpfMigration, + ) -> Result<(), CoreBpfMigrationError> { + datapoint_info!(self.datapoint_name, ("slot", bank.slot, i64)); + + let target = BuiltinConfig::new_checked(bank, program_id, migration)?; + let source = BpfUpgradeableConfig::new_checked(bank, &self.source_program_id)?; + + // Attempt serialization first before touching the bank + let new_target_program_account = create_new_target_program_account(&target, &source)?; + + // Burn lamports from the target program account + bank.capitalization + .fetch_sub(target.program_account.lamports, Relaxed); + + // Replace the native program account with the created to point to the new data + // account and clear the source program account + bank.store_account(&target.program_address, &new_target_program_account); + bank.store_account(&source.program_address, &AccountSharedData::default()); + + // Copy the upgradeable BPF program's data account into the native + // program's data address, which is checked to be empty, then clear the + // upgradeable BPF program's data account. + bank.store_account(&target.program_data_address, &source.program_data_account); + bank.store_account(&source.program_data_address, &AccountSharedData::default()); + + // Update the account data size delta. + bank.calculate_and_update_accounts_data_size_delta_off_chain( + target.total_data_size, + source.total_data_size, + ); + + // Remove the built-in program from the bank's list of built-ins + bank.builtin_programs.remove(&target.program_address); + + // Unload the programs from the bank's cache + bank.loaded_programs_cache + .write() + .unwrap() + .remove_programs([source.program_address, target.program_address].into_iter()); + + Ok(()) + } +} diff --git a/runtime/src/bank/builtins/prototypes.rs b/runtime/src/bank/builtins/prototypes.rs index 9ccc5297516646..f1ec7f5e87c6b4 100644 --- a/runtime/src/bank/builtins/prototypes.rs +++ b/runtime/src/bank/builtins/prototypes.rs @@ -1,6 +1,11 @@ +#![allow(dead_code)] // Removed in later commit use { - super::core_bpf_migration::CoreBpfMigrationConfig, - solana_program_runtime::invoke_context::BuiltinFunctionWithContext, solana_sdk::pubkey::Pubkey, + super::core_bpf_migration::{ + error::CoreBpfMigrationError, CoreBpfMigration, CoreBpfMigrationConfig, + }, + crate::bank::Bank, + solana_program_runtime::invoke_context::BuiltinFunctionWithContext, + solana_sdk::pubkey::Pubkey, }; /// Transitions of built-in programs at epoch boundaries when features are activated. @@ -23,6 +28,19 @@ impl std::fmt::Debug for BuiltinPrototype { } } +impl BuiltinPrototype { + pub(crate) fn migrate_to_core_bpf(&self, bank: &mut Bank) -> Result<(), CoreBpfMigrationError> { + if let Some(config) = &self.core_bpf_migration { + config.migrate_builtin_to_core_bpf( + bank, + &self.program_id, + CoreBpfMigration::Builtin, + )?; + } + Ok(()) + } +} + #[cfg(RUSTC_WITH_SPECIALIZATION)] impl solana_frozen_abi::abi_example::AbiExample for BuiltinPrototype { fn example() -> Self { @@ -60,3 +78,16 @@ impl std::fmt::Debug for EphemeralBuiltinPrototype { builder.finish() } } + +impl EphemeralBuiltinPrototype { + pub(crate) fn migrate_to_core_bpf(&self, bank: &mut Bank) -> Result<(), CoreBpfMigrationError> { + if let Some(config) = &self.core_bpf_migration { + config.migrate_builtin_to_core_bpf( + bank, + &self.program_id, + CoreBpfMigration::Ephemeral, + )?; + } + Ok(()) + } +}