diff --git a/programs/mpl-asset/src/error.rs b/programs/mpl-asset/src/error.rs index b0e48b35..dd0ceaf8 100644 --- a/programs/mpl-asset/src/error.rs +++ b/programs/mpl-asset/src/error.rs @@ -27,6 +27,10 @@ pub enum MplAssetError { /// 4 - Plugin not found #[error("Plugin not found")] PluginNotFound, + + /// 5 - Numerical Overflow + #[error("Numerical Overflow")] + NumericalOverflow, } impl PrintProgramError for MplAssetError { diff --git a/programs/mpl-asset/src/plugins/delegate.rs b/programs/mpl-asset/src/plugins/delegate.rs index 87b6dba8..df980666 100644 --- a/programs/mpl-asset/src/plugins/delegate.rs +++ b/programs/mpl-asset/src/plugins/delegate.rs @@ -1,6 +1,23 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use crate::{state::Key, utils::DataBlob}; + #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] pub struct Delegate { - frozen: bool, + key: Key, // 1 + frozen: bool, // 1 +} + +impl DataBlob for Delegate { + fn get_initial_size() -> usize { + 2 + } + + fn get_size(&self) -> usize { + 2 + } + + fn key() -> crate::state::Key { + Key::Delegate + } } diff --git a/programs/mpl-asset/src/plugins/utils.rs b/programs/mpl-asset/src/plugins/utils.rs index c867e986..f32e0b05 100644 --- a/programs/mpl-asset/src/plugins/utils.rs +++ b/programs/mpl-asset/src/plugins/utils.rs @@ -1,4 +1,4 @@ -use borsh::BorshDeserialize; +use borsh::{BorshDeserialize, BorshSerialize}; use mpl_utils::resize_or_reallocate_account_raw; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, @@ -10,7 +10,7 @@ use crate::{ utils::DataBlob, }; -use super::{Plugin, PluginRegistry}; +use super::{Plugin, PluginRegistry, RegistryData}; /// Create plugin header and registry if it doesn't exist pub fn create_idempotent<'a>( @@ -28,7 +28,7 @@ pub fn create_idempotent<'a>( // They don't exist, so create them. let header = PluginHeader { key: Key::PluginHeader, - plugin_map_offset: asset.get_size() + PluginHeader::get_initial_size(), + plugin_registry_offset: asset.get_size() + PluginHeader::get_initial_size(), }; let registry = PluginRegistry { key: Key::PluginRegistry, @@ -40,11 +40,11 @@ pub fn create_idempotent<'a>( account, payer, system_program, - header.plugin_map_offset + PluginRegistry::get_initial_size(), + header.plugin_registry_offset + PluginRegistry::get_initial_size(), )?; header.save(account, asset.get_size())?; - registry.save(account, header.plugin_map_offset)?; + registry.save(account, header.plugin_registry_offset)?; } Ok(()) @@ -70,7 +70,8 @@ pub fn fetch_plugin( let asset = Asset::deserialize(&mut bytes)?; let header = PluginHeader::load(account, asset.get_size())?; - let PluginRegistry { registry, .. } = PluginRegistry::load(account, header.plugin_map_offset)?; + let PluginRegistry { registry, .. } = + PluginRegistry::load(account, header.plugin_registry_offset)?; // Find the plugin in the registry. let plugin_data = registry @@ -92,30 +93,94 @@ pub fn list_plugins(account: &AccountInfo) -> Result, ProgramError> { let asset = Asset::deserialize(&mut bytes)?; let header = PluginHeader::load(account, asset.get_size())?; - let PluginRegistry { registry, .. } = PluginRegistry::load(account, header.plugin_map_offset)?; + let PluginRegistry { registry, .. } = + PluginRegistry::load(account, header.plugin_registry_offset)?; Ok(registry.iter().map(|(key, _)| *key).collect()) } /// Add a plugin into the registry -pub fn add_plugin(plugin: &Plugin, authority: Authority, account: &AccountInfo) -> ProgramResult { - let mut bytes: &[u8] = &(*account.data).borrow(); - let asset = Asset::deserialize(&mut bytes)?; +pub fn add_plugin<'a>( + plugin: &Plugin, + authority: Authority, + account: &AccountInfo<'a>, + payer: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, +) -> ProgramResult { + let asset = { + let mut bytes: &[u8] = &(*account.data).borrow(); + Asset::deserialize(&mut bytes)? + }; //TODO: Bytemuck this. let mut header = PluginHeader::load(account, asset.get_size())?; - let mut plugin_registry = PluginRegistry::load(account, header.plugin_map_offset)?; + let mut plugin_registry = PluginRegistry::load(account, header.plugin_registry_offset)?; - let plugin_type = match plugin { + let (plugin_size, key) = match plugin { Plugin::Reserved => todo!(), Plugin::Royalties => todo!(), Plugin::MasterEdition => todo!(), Plugin::PrintEdition => todo!(), - Plugin::Delegate(delegate) => todo!(), + Plugin::Delegate(delegate) => (delegate.get_size(), Key::Delegate), Plugin::Inscription => todo!(), }; - // plugin_registry.registry.push(()); + if let Some((_, registry_data)) = plugin_registry + .registry + .iter_mut() + .find(|(search_key, registry_data)| search_key == &key) + { + registry_data.authorities.push(authority); + + let authority_bytes = authority.try_to_vec()?; + + let new_size = account + .data_len() + .checked_add(authority_bytes.len()) + .ok_or(MplAssetError::NumericalOverflow)?; + resize_or_reallocate_account_raw(account, payer, system_program, new_size)?; + + plugin_registry.save(account, header.plugin_registry_offset); + } else { + let authority_bytes = authority.try_to_vec()?; + + let new_size = account + .data_len() + .checked_add(plugin_size) + .ok_or(MplAssetError::NumericalOverflow)? + .checked_add(authority_bytes.len()) + .ok_or(MplAssetError::NumericalOverflow)?; + + let old_registry_offset = header.plugin_registry_offset; + let new_registry_offset = header + .plugin_registry_offset + .checked_add(plugin_size) + .ok_or(MplAssetError::NumericalOverflow)? + .checked_add(authority_bytes.len()) + .ok_or(MplAssetError::NumericalOverflow)?; + + header.plugin_registry_offset = new_registry_offset; + plugin_registry.registry.push(( + key, + RegistryData { + offset: old_registry_offset, + authorities: vec![authority], + }, + )); + + resize_or_reallocate_account_raw(account, payer, system_program, new_size); + + header.save(account, asset.get_size()); + match plugin { + Plugin::Reserved => todo!(), + Plugin::Royalties => todo!(), + Plugin::MasterEdition => todo!(), + Plugin::PrintEdition => todo!(), + Plugin::Delegate(delegate) => delegate.save(account, old_registry_offset), + Plugin::Inscription => todo!(), + }; + plugin_registry.save(account, new_registry_offset); + } Ok(()) } diff --git a/programs/mpl-asset/src/state/mod.rs b/programs/mpl-asset/src/state/mod.rs index 236c9309..5fa1884b 100644 --- a/programs/mpl-asset/src/state/mod.rs +++ b/programs/mpl-asset/src/state/mod.rs @@ -54,6 +54,7 @@ pub enum Key { HashedCollection, PluginHeader, PluginRegistry, + Delegate, } #[repr(C)] diff --git a/programs/mpl-asset/src/state/plugin_header.rs b/programs/mpl-asset/src/state/plugin_header.rs index 85979851..d5bd29da 100644 --- a/programs/mpl-asset/src/state/plugin_header.rs +++ b/programs/mpl-asset/src/state/plugin_header.rs @@ -6,7 +6,7 @@ use crate::{state::Key, utils::DataBlob}; #[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] pub struct PluginHeader { pub key: Key, - pub plugin_map_offset: usize, // 8 + pub plugin_registry_offset: usize, // 8 } impl DataBlob for PluginHeader {