diff --git a/programs/mpl-core/src/plugins/external_plugins.rs b/programs/mpl-core/src/plugins/external_plugins.rs index 0f4f4770..0b0fdfba 100644 --- a/programs/mpl-core/src/plugins/external_plugins.rs +++ b/programs/mpl-core/src/plugins/external_plugins.rs @@ -1,13 +1,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, - pubkey::Pubkey, -}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use strum::EnumCount; use crate::{ error::MplCoreError, - state::{AssetV1, SolanaAccount}, + state::{AssetV1, PluginSolanaAccount, SolanaAccount}, }; use super::{ @@ -180,25 +177,10 @@ impl ExternalPlugin { ExternalPlugin::DataStore(data_store) => data_store.validate_add_external_plugin(ctx), } } - - /// Load and deserialize a plugin from an offset in the account. - pub fn load(account: &AccountInfo, offset: usize) -> Result { - let mut bytes: &[u8] = &(*account.data).borrow()[offset..]; - Self::deserialize(&mut bytes).map_err(|error| { - msg!("Error: {}", error); - MplCoreError::DeserializationError.into() - }) - } - - /// Save and serialize a plugin to an offset in the account. - pub fn save(&self, account: &AccountInfo, offset: usize) -> ProgramResult { - borsh::to_writer(&mut account.data.borrow_mut()[offset..], self).map_err(|error| { - msg!("Error: {}", error); - MplCoreError::SerializationError.into() - }) - } } +impl PluginSolanaAccount for ExternalPlugin {} + impl From<&ExternalPluginInitInfo> for ExternalPlugin { fn from(init_info: &ExternalPluginInitInfo) -> Self { match init_info { diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 02033f7f..4c293ed7 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use crate::{ error::MplCoreError, - state::{Authority, Key}, + state::{Authority, Key, PluginSolanaAccount}, }; use super::{ diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index d09156ce..c3ea7ec7 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -43,15 +43,9 @@ pub use utils::*; use borsh::{BorshDeserialize, BorshSerialize}; use num_derive::ToPrimitive; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, -}; use strum::EnumCount; -use crate::{ - error::MplCoreError, - state::{Authority, Compressible, DataBlob}, -}; +use crate::state::{Authority, Compressible, DataBlob, PluginSolanaAccount}; /// Definition of the plugin variants, each containing a link to the plugin struct. #[repr(C)] @@ -86,25 +80,10 @@ impl Plugin { pub fn manager(&self) -> Authority { PluginType::from(self).manager() } - - /// Load and deserialize a plugin from an offset in the account. - pub fn load(account: &AccountInfo, offset: usize) -> Result { - let mut bytes: &[u8] = &(*account.data).borrow()[offset..]; - Self::deserialize(&mut bytes).map_err(|error| { - msg!("Error: {}", error); - MplCoreError::DeserializationError.into() - }) - } - - /// Save and serialize a plugin to an offset in the account. - pub fn save(&self, account: &AccountInfo, offset: usize) -> ProgramResult { - borsh::to_writer(&mut account.data.borrow_mut()[offset..], self).map_err(|error| { - msg!("Error: {}", error); - MplCoreError::SerializationError.into() - }) - } } +impl PluginSolanaAccount for Plugin {} + impl Compressible for Plugin {} /// List of first party plugin types. diff --git a/programs/mpl-core/src/plugins/utils.rs b/programs/mpl-core/src/plugins/utils.rs index c1a974a3..f3e1fba9 100644 --- a/programs/mpl-core/src/plugins/utils.rs +++ b/programs/mpl-core/src/plugins/utils.rs @@ -8,7 +8,7 @@ use std::collections::HashSet; use crate::{ error::MplCoreError, plugins::{ExternalCheckResult, HookableLifecycleEvent}, - state::{AssetV1, Authority, CoreAsset, DataBlob, Key, SolanaAccount}, + state::{AssetV1, Authority, CoreAsset, DataBlob, Key, PluginSolanaAccount, SolanaAccount}, utils::resize_or_reallocate_account, }; diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index bda8ca6f..edd34841 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -1,8 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; use mpl_utils::assert_signer; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_memory::sol_memcpy, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, @@ -13,8 +11,8 @@ use crate::{ }, state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount, UpdateAuthority}, utils::{ - load_key, resize_or_reallocate_account, resolve_authority, validate_asset_permissions, - validate_collection_permissions, + load_key, move_plugins_and_registry, resize_or_reallocate_account, resolve_authority, + validate_asset_permissions, validate_collection_permissions, }, }; @@ -70,7 +68,7 @@ pub(crate) fn update<'a>(accounts: &'a [AccountInfo<'a>], args: UpdateV1Args) -> // Increment sequence number and save only if it is `Some(_)`. asset.increment_seq_and_save(ctx.accounts.asset)?; - let asset_size = asset.get_size() as isize; + let asset_size = asset.get_size(); let mut dirty = false; if let Some(new_update_authority) = args.new_update_authority { @@ -153,7 +151,7 @@ pub(crate) fn update_collection<'a>( Some(HookableLifecycleEvent::Update), )?; - let collection_size = collection.get_size() as isize; + let collection_size = collection.get_size(); let mut dirty = false; if let Some(new_update_authority) = ctx.accounts.new_update_authority { @@ -187,7 +185,7 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( core: T, plugin_header: &Option, plugin_registry: &Option, - core_size: isize, + core_size: usize, account: &AccountInfo<'a>, payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, @@ -196,70 +194,32 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( (plugin_header.clone(), plugin_registry.clone()) { // The new size of the asset and new offset of the plugin header. - let new_core_size = core.get_size() as isize; + let new_core_size = core.get_size(); // The difference in size between the new and old asset which is used to calculate the new size of the account. - let size_diff = new_core_size - .checked_sub(core_size) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new size of the account. - let new_size = (account.data_len() as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new offset of the plugin registry is the old offset plus the size difference. - let registry_offset = plugin_header.plugin_registry_offset; - let new_registry_offset = (registry_offset as isize) - .checked_add(size_diff) + let size_diff = (new_core_size as isize) + .checked_sub(core_size as isize) .ok_or(MplCoreError::NumericalOverflow)?; - plugin_header.plugin_registry_offset = new_registry_offset as usize; // The offset of the first plugin is the core size plus the size of the plugin header. let plugin_offset = core_size - .checked_add(plugin_header.get_size() as isize) + .checked_add(plugin_header.get_size()) .ok_or(MplCoreError::NumericalOverflow)?; - let new_plugin_offset = plugin_offset - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // //TODO: This is memory intensive, we should use memmove instead probably. - let src = account.data.borrow()[(plugin_offset as usize)..registry_offset].to_vec(); - - resize_or_reallocate_account(account, payer, system_program, new_size as usize)?; - - sol_memcpy( - &mut account.data.borrow_mut()[(new_plugin_offset as usize)..], - &src, - src.len(), - ); - - plugin_header.save(account, new_core_size as usize)?; - - // Move offsets for existing registry records. - for record in &mut plugin_registry.external_registry { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - - for record in &mut plugin_registry.registry { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - - plugin_registry.save(account, new_registry_offset as usize)?; + move_plugins_and_registry( + new_core_size, + size_diff, + plugin_offset, + plugin_offset, + &mut plugin_header, + &mut plugin_registry, + account, + payer, + system_program, + )?; } else { resize_or_reallocate_account(account, payer, system_program, core.get_size())?; } - core.save(account, 0)?; - - Ok(()) + core.save(account, 0) } diff --git a/programs/mpl-core/src/processor/update_external_plugin.rs b/programs/mpl-core/src/processor/update_external_plugin.rs index f5eaf667..193b763e 100644 --- a/programs/mpl-core/src/processor/update_external_plugin.rs +++ b/programs/mpl-core/src/processor/update_external_plugin.rs @@ -1,8 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; use mpl_utils::assert_signer; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_memory::sol_memcpy, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, @@ -13,9 +11,9 @@ use crate::{ fetch_wrapped_external_plugin, find_external_plugin, ExternalPlugin, ExternalPluginKey, ExternalPluginUpdateInfo, Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, }, - state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount}, + state::{AssetV1, CollectionV1, DataBlob, Key, PluginSolanaAccount, SolanaAccount}, utils::{ - load_key, resize_or_reallocate_account, resolve_authority, validate_asset_permissions, + load_key, move_plugins_and_registry, resolve_authority, validate_asset_permissions, validate_collection_permissions, }, }; @@ -180,68 +178,26 @@ fn process_update_external_plugin<'a, T: DataBlob + SolanaAccount>( let new_plugin_data = new_plugin.try_to_vec()?; // The difference in size between the new and old account which is used to calculate the new size of the account. - let plugin_size = plugin_data.len() as isize; + let plugin_size = plugin_data.len(); let size_diff = (new_plugin_data.len() as isize) - .checked_sub(plugin_size) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new size of the account. - let new_size = (account.data_len() as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new offset of the plugin registry is the old offset plus the size difference. - let registry_offset = plugin_header.plugin_registry_offset; - let new_registry_offset = (registry_offset as isize) - .checked_add(size_diff) + .checked_sub(plugin_size as isize) .ok_or(MplCoreError::NumericalOverflow)?; - plugin_header.plugin_registry_offset = new_registry_offset as usize; - // The offset of the first plugin is the plugin offset plus the size of the plugin. - let next_plugin_offset = (registry_record.offset as isize) + let data_to_move_location = (registry_record.offset) .checked_add(plugin_size) .ok_or(MplCoreError::NumericalOverflow)?; - let new_next_plugin_offset = next_plugin_offset - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // //TODO: This is memory intensive, we should use memmove instead probably. - let src = account.data.borrow()[(next_plugin_offset as usize)..registry_offset].to_vec(); - - resize_or_reallocate_account(account, payer, system_program, new_size as usize)?; - - sol_memcpy( - &mut account.data.borrow_mut()[(new_next_plugin_offset as usize)..], - &src, - src.len(), - ); - - plugin_header.save(account, core.get_size())?; - - // Move offsets for existing registry records. - for record in &mut plugin_registry.external_registry { - if registry_record.offset < record.offset { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - } - - for record in &mut plugin_registry.registry { - if registry_record.offset < record.offset { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - } - - plugin_registry.save(account, new_registry_offset as usize)?; - new_plugin.save(account, registry_record.offset)?; + move_plugins_and_registry( + core.get_size(), + size_diff, + registry_record.offset, + data_to_move_location, + &mut plugin_header, + &mut plugin_registry, + account, + payer, + system_program, + )?; - Ok(()) + new_plugin.save(account, registry_record.offset) } diff --git a/programs/mpl-core/src/processor/update_plugin.rs b/programs/mpl-core/src/processor/update_plugin.rs index b1ed0114..f5c74c08 100644 --- a/programs/mpl-core/src/processor/update_plugin.rs +++ b/programs/mpl-core/src/processor/update_plugin.rs @@ -1,16 +1,14 @@ use borsh::{BorshDeserialize, BorshSerialize}; use mpl_utils::assert_signer; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_memory::sol_memcpy, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::{UpdateCollectionPluginV1Accounts, UpdatePluginV1Accounts}, plugins::{Plugin, PluginHeaderV1, PluginRegistryV1, PluginType}, - state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount}, + state::{AssetV1, CollectionV1, DataBlob, Key, PluginSolanaAccount, SolanaAccount}, utils::{ - load_key, resize_or_reallocate_account, resolve_authority, validate_asset_permissions, + load_key, move_plugins_and_registry, resolve_authority, validate_asset_permissions, validate_collection_permissions, }, }; @@ -157,68 +155,26 @@ fn process_update_plugin<'a, T: DataBlob + SolanaAccount>( let new_plugin_data = new_plugin.try_to_vec()?; // The difference in size between the new and old account which is used to calculate the new size of the account. - let plugin_size = plugin_data.len() as isize; + let plugin_size = plugin_data.len(); let size_diff = (new_plugin_data.len() as isize) - .checked_sub(plugin_size) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new size of the account. - let new_size = (account.data_len() as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // The new offset of the plugin registry is the old offset plus the size difference. - let registry_offset = plugin_header.plugin_registry_offset; - let new_registry_offset = (registry_offset as isize) - .checked_add(size_diff) + .checked_sub(plugin_size as isize) .ok_or(MplCoreError::NumericalOverflow)?; - plugin_header.plugin_registry_offset = new_registry_offset as usize; - // The offset of the first plugin is the plugin offset plus the size of the plugin. - let next_plugin_offset = (registry_record.offset as isize) + let data_to_move_location = (registry_record.offset) .checked_add(plugin_size) .ok_or(MplCoreError::NumericalOverflow)?; - let new_next_plugin_offset = next_plugin_offset - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - // //TODO: This is memory intensive, we should use memmove instead probably. - let src = account.data.borrow()[(next_plugin_offset as usize)..registry_offset].to_vec(); - - resize_or_reallocate_account(account, payer, system_program, new_size as usize)?; - - sol_memcpy( - &mut account.data.borrow_mut()[(new_next_plugin_offset as usize)..], - &src, - src.len(), - ); - - plugin_header.save(account, core.get_size())?; - - // Move offsets for existing registry records. - for record in &mut plugin_registry.external_registry { - if registry_record.offset < record.offset { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - } - - for record in &mut plugin_registry.registry { - if registry_record.offset < record.offset { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - } - - plugin_registry.save(account, new_registry_offset as usize)?; - new_plugin.save(account, registry_record.offset)?; + move_plugins_and_registry( + core.get_size(), + size_diff, + registry_record.offset, + data_to_move_location, + &mut plugin_header, + &mut plugin_registry, + account, + payer, + system_program, + )?; - Ok(()) + new_plugin.save(account, registry_record.offset) } diff --git a/programs/mpl-core/src/state/traits.rs b/programs/mpl-core/src/state/traits.rs index 008b6c45..31138424 100644 --- a/programs/mpl-core/src/state/traits.rs +++ b/programs/mpl-core/src/state/traits.rs @@ -44,6 +44,26 @@ pub trait SolanaAccount: BorshSerialize + BorshDeserialize { } } +/// A trait for Solana accounts without a key. +pub trait PluginSolanaAccount: BorshSerialize + BorshDeserialize { + /// Load and deserialize a plugin from an offset in the account. + fn load(account: &AccountInfo, offset: usize) -> Result { + let mut bytes: &[u8] = &(*account.data).borrow()[offset..]; + Self::deserialize(&mut bytes).map_err(|error| { + msg!("Error: {}", error); + MplCoreError::DeserializationError.into() + }) + } + + /// Save and serialize a plugin to an offset in the account. + fn save(&self, account: &AccountInfo, offset: usize) -> ProgramResult { + borsh::to_writer(&mut account.data.borrow_mut()[offset..], self).map_err(|error| { + msg!("Error: {}", error); + MplCoreError::SerializationError.into() + }) + } +} + /// A trait for data that can be compressed. pub trait Compressible: BorshSerialize + BorshDeserialize { /// Get the hash of the compressed data. diff --git a/programs/mpl-core/src/utils.rs b/programs/mpl-core/src/utils.rs index 81458c40..c75a2a97 100644 --- a/programs/mpl-core/src/utils.rs +++ b/programs/mpl-core/src/utils.rs @@ -725,3 +725,68 @@ pub(crate) fn resolve_authority<'a>( None => Ok(payer), } } + +#[allow(clippy::too_many_arguments)] +pub(crate) fn move_plugins_and_registry<'a>( + header_location: usize, + size_diff: isize, + first_plugin_location: usize, + data_to_move_location: usize, + plugin_header: &mut PluginHeaderV1, + plugin_registry: &mut PluginRegistryV1, + account: &AccountInfo<'a>, + payer: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, +) -> ProgramResult { + // The new size of the account. + let new_size = (account.data_len() as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)?; + + // The new offset of the plugin registry is the old offset plus the size difference. + let registry_offset = plugin_header.plugin_registry_offset; + let new_registry_offset = (registry_offset as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)?; + plugin_header.plugin_registry_offset = new_registry_offset as usize; + + let new_data_location = (data_to_move_location as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)?; + + // //TODO: This is memory intensive, we should use memmove instead probably. + let src = account.data.borrow()[(data_to_move_location)..registry_offset].to_vec(); + + resize_or_reallocate_account(account, payer, system_program, new_size as usize)?; + + sol_memcpy( + &mut account.data.borrow_mut()[(new_data_location as usize)..], + &src, + src.len(), + ); + + plugin_header.save(account, header_location)?; + + // Move offsets for existing registry records. + for record in &mut plugin_registry.external_registry { + if first_plugin_location == data_to_move_location || first_plugin_location < record.offset { + let new_offset = (record.offset as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)?; + + record.offset = new_offset as usize; + } + } + + for record in &mut plugin_registry.registry { + if first_plugin_location == data_to_move_location || first_plugin_location < record.offset { + let new_offset = (record.offset as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)?; + + record.offset = new_offset as usize; + } + } + + plugin_registry.save(account, new_registry_offset as usize) +}