From 2274735604dcfed4e2506ba4947806b4e4443892 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 25 Nov 2024 14:41:27 -0500 Subject: [PATCH 01/10] Implementing get_size for everything and switching to that over serialized data size. --- .../internal/authority_managed/attributes.rs | 21 ++++++-- .../authority_managed/master_edition.rs | 22 ++++++-- .../internal/authority_managed/royalties.rs | 53 ++++++++++++++++--- .../authority_managed/update_delegate.rs | 6 +-- .../authority_managed/verified_creators.rs | 27 ++++++++-- .../internal/owner_managed/autograph.rs | 30 +++++++++-- .../src/plugins/internal/permanent/edition.rs | 17 +++++- programs/mpl-core/src/plugins/lifecycle.rs | 2 +- programs/mpl-core/src/plugins/mod.rs | 42 ++++++++++++++- .../mpl-core/src/plugins/plugin_registry.rs | 10 ++++ programs/mpl-core/src/plugins/utils.rs | 33 ++++-------- .../processor/add_external_plugin_adapter.rs | 9 ++-- programs/mpl-core/src/processor/add_plugin.rs | 3 +- programs/mpl-core/src/processor/create.rs | 27 +++++----- .../src/processor/create_collection.rs | 27 +++++----- .../write_external_plugin_adapter_data.rs | 6 +-- programs/mpl-core/src/state/mod.rs | 14 +++++ programs/mpl-core/src/utils/compression.rs | 3 +- programs/mpl-core/src/utils/mod.rs | 1 + 19 files changed, 265 insertions(+), 88 deletions(-) diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs index 49444977..b228131b 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs @@ -6,9 +6,19 @@ use borsh::{BorshDeserialize, BorshSerialize}; #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Default)] pub struct Attribute { /// The Key of the attribute. - pub key: String, // 4 + pub key: String, // 4 + len /// The Value of the attribute. - pub value: String, // 4 + pub value: String, // 4 + len +} + +impl DataBlob for Attribute { + fn get_initial_size() -> usize { + 4 + 4 + } + + fn get_size(&self) -> usize { + 4 + self.key.len() + 4 + self.value.len() + } } /// The Attributes plugin allows the authority to add arbitrary Key-Value pairs to the asset. @@ -16,7 +26,7 @@ pub struct Attribute { #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Default)] pub struct Attributes { /// A vector of Key-Value pairs. - pub attribute_list: Vec, // 4 + pub attribute_list: Vec, // 4 + len * Attribute } impl Attributes { @@ -32,7 +42,10 @@ impl DataBlob for Attributes { } fn get_size(&self) -> usize { - 4 // TODO: Implement this. + 4 + self + .attribute_list + .iter() + .fold(0, |acc, attr| acc + attr.get_size()) } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs index ed008ed4..21a18921 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs @@ -1,6 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use crate::plugins::PluginValidation; +use crate::{plugins::PluginValidation, state::DataBlob}; /// The master edition plugin allows the creator to specify details on the master edition including max supply, name, and uri. /// The default authority for this plugin is the creator. @@ -8,11 +8,25 @@ use crate::plugins::PluginValidation; #[derive(Clone, BorshSerialize, BorshDeserialize, Default, Debug, PartialEq, Eq)] pub struct MasterEdition { /// The max supply of editions - pub max_supply: Option, + pub max_supply: Option, // 1 + optional 4 /// optional master edition name - pub name: Option, + pub name: Option, // 1 + optional 4 /// optional master edition uri - pub uri: Option, + pub uri: Option, // 1 + optional 4 } impl PluginValidation for MasterEdition {} + +impl DataBlob for MasterEdition { + fn get_initial_size() -> usize { + 1 + 1 + 1 + } + + fn get_size(&self) -> usize { + 1 + self.max_supply.map_or(0, |_| 4) + + 1 + + self.name.as_ref().map_or(0, |name| 4 + name.len()) + + 1 + + self.uri.as_ref().map_or(0, |uri| 4 + uri.len()) + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs index 97b863fa..209fb227 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs @@ -8,34 +8,71 @@ use crate::error::MplCoreError; use crate::plugins::{ abstain, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, }; +use crate::state::DataBlob; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] pub struct Creator { - address: Pubkey, - percentage: u8, + address: Pubkey, // 32 + percentage: u8, // 1 +} + +impl DataBlob for Creator { + fn get_initial_size() -> usize { + 33 + } + + fn get_size(&self) -> usize { + 33 + } } /// The rule set for an asset indicating where it is allowed to be transferred. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] pub enum RuleSet { /// No rules are enforced. - None, + None, // 1 /// Allow list of programs that are allowed to transfer, receive, or send the asset. - ProgramAllowList(Vec), + ProgramAllowList(Vec), // 4 /// Deny list of programs that are not allowed to transfer, receive, or send the asset. - ProgramDenyList(Vec), + ProgramDenyList(Vec), // 4 +} + +impl DataBlob for RuleSet { + fn get_initial_size() -> usize { + 1 + } + + fn get_size(&self) -> usize { + 1 + match self { + RuleSet::ProgramAllowList(allow_list) => 4 + allow_list.len() * 32, + RuleSet::ProgramDenyList(deny_list) => 4 + deny_list.len() * 32, + RuleSet::None => 0, + } + } } /// Traditional royalties structure for an asset. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct Royalties { /// The percentage of royalties to be paid to the creators. - basis_points: u16, + basis_points: u16, // 2 /// A list of creators to receive royalties. - creators: Vec, + creators: Vec, // 4 /// The rule set for the asset to enforce royalties. - rule_set: RuleSet, + rule_set: RuleSet, // 1 +} + +impl DataBlob for Royalties { + fn get_initial_size() -> usize { + 1 + } + + fn get_size(&self) -> usize { + 2 // basis_points + + (4 + self.creators.len() * Creator::get_initial_size()) // creators + + self.rule_set.get_size() // rule_set + } } fn validate_royalties(royalties: &Royalties) -> Result { diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs index d8a952d2..d305a00d 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs @@ -19,7 +19,7 @@ use crate::plugins::{ #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] pub struct UpdateDelegate { /// Additional update delegates. Not currently available to be used. - pub additional_delegates: Vec, // 4 + pub additional_delegates: Vec, // 4 + len * 32 } impl UpdateDelegate { @@ -39,11 +39,11 @@ impl Default for UpdateDelegate { impl DataBlob for UpdateDelegate { fn get_initial_size() -> usize { - 0 + 4 } fn get_size(&self) -> usize { - 0 + 4 + self.additional_delegates.len() * 32 } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs index a942c0e4..e90d7706 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs @@ -8,19 +8,40 @@ use crate::error::MplCoreError; use crate::plugins::{ abstain, Plugin, PluginValidation, PluginValidationContext, ValidationResult, }; +use crate::state::DataBlob; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] pub struct VerifiedCreatorsSignature { - address: Pubkey, - verified: bool, + address: Pubkey, // 32 + verified: bool, // 1 +} + +impl DataBlob for VerifiedCreatorsSignature { + fn get_initial_size() -> usize { + 32 + 1 + } + + fn get_size(&self) -> usize { + 32 + 1 + } } /// Structure for storing verified creators, often used in conjunction with the Royalties plugin #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct VerifiedCreators { /// A list of signatures - signatures: Vec, + signatures: Vec, // 4 + len * VerifiedCreatorsSignature +} + +impl DataBlob for VerifiedCreators { + fn get_initial_size() -> usize { + 4 + } + + fn get_size(&self) -> usize { + 4 + self.signatures.len() * VerifiedCreatorsSignature::get_initial_size() + } } struct SignatureChangeIndices { diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs index abf621df..b7a923f3 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs @@ -8,20 +8,31 @@ use crate::{ plugins::{ abstain, approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult, }, + state::DataBlob, }; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] pub struct AutographSignature { - address: Pubkey, - message: String, + address: Pubkey, // 32 + message: String, // 4 + len +} + +impl DataBlob for AutographSignature { + fn get_initial_size() -> usize { + 32 + 4 + } + + fn get_size(&self) -> usize { + 32 + 4 + self.message.len() + } } /// Structure for an autograph book, often used in conjunction with the Royalties plugin for verified creators #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct Autograph { /// A list of signatures with option message - signatures: Vec, + signatures: Vec, // 4 + len * Autograph len } fn validate_autograph( @@ -122,3 +133,16 @@ impl PluginValidation for Autograph { } } } + +impl DataBlob for Autograph { + fn get_initial_size() -> usize { + 4 + } + + fn get_size(&self) -> usize { + 4 + self + .signatures + .iter() + .fold(0, |acc, sig| acc + sig.get_size()) + } +} diff --git a/programs/mpl-core/src/plugins/internal/permanent/edition.rs b/programs/mpl-core/src/plugins/internal/permanent/edition.rs index 576c18e3..7ea3b475 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/edition.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/edition.rs @@ -1,8 +1,11 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; -use crate::plugins::{ - abstain, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, +use crate::{ + plugins::{ + abstain, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, + }, + state::DataBlob, }; /// The edition plugin allows the creator to set an edition number on the asset @@ -43,3 +46,13 @@ impl PluginValidation for Edition { } } } + +impl DataBlob for Edition { + fn get_initial_size() -> usize { + 4 + } + + fn get_size(&self) -> usize { + 4 + } +} diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index d3810c16..2f47c5ed 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -8,7 +8,7 @@ use crate::{ state::{Authority, Key, UpdateAuthority}, }; -use crate::plugins::{ +use super::{ ExternalPluginAdapter, ExternalPluginAdapterKey, ExternalRegistryRecord, Plugin, PluginType, RegistryRecord, }; diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index 29b4845d..c93f9820 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -88,6 +88,38 @@ impl Plugin { impl Compressible for Plugin {} +impl DataBlob for Plugin { + fn get_initial_size() -> usize { + 1 + } + + fn get_size(&self) -> usize { + 1 + match self { + Plugin::Royalties(royalties) => royalties.get_size(), + Plugin::FreezeDelegate(freeze_delegate) => freeze_delegate.get_size(), + Plugin::BurnDelegate(burn_delegate) => burn_delegate.get_size(), + Plugin::TransferDelegate(transfer_delegate) => transfer_delegate.get_size(), + Plugin::UpdateDelegate(update_delegate) => update_delegate.get_size(), + Plugin::PermanentFreezeDelegate(permanent_freeze_delegate) => { + permanent_freeze_delegate.get_size() + } + Plugin::Attributes(attributes) => attributes.get_size(), + Plugin::PermanentTransferDelegate(permanent_transfer_delegate) => { + permanent_transfer_delegate.get_size() + } + Plugin::PermanentBurnDelegate(permanent_burn_delegate) => { + permanent_burn_delegate.get_size() + } + Plugin::Edition(edition) => edition.get_size(), + Plugin::MasterEdition(master_edition) => master_edition.get_size(), + Plugin::AddBlocker(add_blocker) => add_blocker.get_size(), + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata.get_size(), + Plugin::VerifiedCreators(verified_creators) => verified_creators.get_size(), + Plugin::Autograph(autograph) => autograph.get_size(), + } + } +} + /// List of first party plugin types. #[repr(C)] #[derive( @@ -138,11 +170,11 @@ pub enum PluginType { impl DataBlob for PluginType { fn get_initial_size() -> usize { - 2 + 1 } fn get_size(&self) -> usize { - 2 + 1 } } @@ -198,3 +230,9 @@ pub(crate) struct PluginAuthorityPair { pub(crate) plugin: Plugin, pub(crate) authority: Option, } + +impl From for usize { + fn from(plugin_type: PluginType) -> Self { + plugin_type as usize + } +} diff --git a/programs/mpl-core/src/plugins/plugin_registry.rs b/programs/mpl-core/src/plugins/plugin_registry.rs index 046f1813..134069f3 100644 --- a/programs/mpl-core/src/plugins/plugin_registry.rs +++ b/programs/mpl-core/src/plugins/plugin_registry.rs @@ -146,6 +146,16 @@ impl RegistryRecord { } } +impl DataBlob for RegistryRecord { + fn get_initial_size() -> usize { + 2 + 1 + 8 + } + + fn get_size(&self) -> usize { + 2 + self.authority.get_size() + 8 + } +} + /// A type to store the mapping of third party plugin type to third party plugin header and data. #[repr(C)] #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] diff --git a/programs/mpl-core/src/plugins/utils.rs b/programs/mpl-core/src/plugins/utils.rs index 66d311d8..bf2fdb7b 100644 --- a/programs/mpl-core/src/plugins/utils.rs +++ b/programs/mpl-core/src/plugins/utils.rs @@ -27,7 +27,7 @@ pub fn create_meta_idempotent<'a, T: SolanaAccount + DataBlob>( account: &AccountInfo<'a>, payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, -) -> Result<(T, PluginHeaderV1, PluginRegistryV1), ProgramError> { +) -> Result<(T, usize, PluginHeaderV1, PluginRegistryV1), ProgramError> { let core = T::load(account, 0)?; let header_offset = core.get_size(); @@ -54,13 +54,13 @@ pub fn create_meta_idempotent<'a, T: SolanaAccount + DataBlob>( header.save(account, header_offset)?; registry.save(account, header.plugin_registry_offset)?; - Ok((core, header, registry)) + Ok((core, header_offset, header, registry)) } else { // They exist, so load them. let header = PluginHeaderV1::load(account, header_offset)?; let registry = PluginRegistryV1::load(account, header.plugin_registry_offset)?; - Ok((core, header, registry)) + Ok((core, header_offset, header, registry)) } } @@ -70,7 +70,7 @@ pub fn create_plugin_meta<'a, T: SolanaAccount + DataBlob>( account: &AccountInfo<'a>, payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, -) -> Result<(PluginHeaderV1, PluginRegistryV1), ProgramError> { +) -> Result<(usize, PluginHeaderV1, PluginRegistryV1), ProgramError> { let header_offset = asset.get_size(); // They don't exist, so create them. @@ -94,7 +94,7 @@ pub fn create_plugin_meta<'a, T: SolanaAccount + DataBlob>( header.save(account, header_offset)?; registry.save(account, header.plugin_registry_offset)?; - Ok((header, registry)) + Ok((header_offset, header, registry)) } /// Assert that the Plugin metadata has been initialized. @@ -255,20 +255,19 @@ pub fn list_plugins(account: &AccountInfo) -> Result, ProgramErr } /// Add a plugin to the registry and initialize it. +#[allow(clippy::too_many_arguments)] pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>( plugin: &Plugin, authority: &Authority, + header_offset: usize, plugin_header: &mut PluginHeaderV1, plugin_registry: &mut PluginRegistryV1, account: &AccountInfo<'a>, payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, ) -> ProgramResult { - let core = T::load(account, 0)?; - let header_offset = core.get_size(); let plugin_type = plugin.into(); - let plugin_data = plugin.try_to_vec()?; - let plugin_size = plugin_data.len(); + let plugin_size = plugin.get_size(); // You cannot add a duplicate plugin. if plugin_registry @@ -288,7 +287,7 @@ pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>( }; let size_increase = plugin_size - .checked_add(new_registry_record.try_to_vec()?.len()) + .checked_add(new_registry_record.get_size()) .ok_or(MplCoreError::NumericalOverflow)?; let new_registry_offset = plugin_header @@ -317,7 +316,7 @@ pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>( #[allow(clippy::too_many_arguments)] pub fn initialize_external_plugin_adapter<'a, T: DataBlob + SolanaAccount>( init_info: &ExternalPluginAdapterInitInfo, - core: Option<&T>, + header_offset: usize, plugin_header: &mut PluginHeaderV1, plugin_registry: &mut PluginRegistryV1, account: &AccountInfo<'a>, @@ -325,18 +324,6 @@ pub fn initialize_external_plugin_adapter<'a, T: DataBlob + SolanaAccount>( system_program: &AccountInfo<'a>, appended_data: Option<&[u8]>, ) -> ProgramResult { - let header_offset = match core { - Some(core) => core.get_size(), - None => { - let asset = T::load(account, 0)?; - - if asset.get_size() == account.data_len() { - return Err(MplCoreError::ExternalPluginAdapterNotFound.into()); - } - - asset.get_size() - } - }; let plugin_type = init_info.into(); // Note currently we are blocking adding LifecycleHook and LinkedLifecycleHook external plugin adapters as they diff --git a/programs/mpl-core/src/processor/add_external_plugin_adapter.rs b/programs/mpl-core/src/processor/add_external_plugin_adapter.rs index 9f95acf6..8d9e0aa0 100644 --- a/programs/mpl-core/src/processor/add_external_plugin_adapter.rs +++ b/programs/mpl-core/src/processor/add_external_plugin_adapter.rs @@ -113,7 +113,6 @@ pub(crate) fn add_external_plugin_adapter<'a>( ctx.accounts.payer, ctx.accounts.system_program, &args.init_info, - &asset, ) } @@ -172,7 +171,7 @@ pub(crate) fn add_collection_external_plugin_adapter<'a>( let external_plugin_adapter = ExternalPluginAdapter::from(&args.init_info); // Validate collection permissions. - let (core, _, _) = validate_collection_permissions( + let _ = validate_collection_permissions( accounts, authority, ctx.accounts.collection, @@ -192,7 +191,6 @@ pub(crate) fn add_collection_external_plugin_adapter<'a>( ctx.accounts.payer, ctx.accounts.system_program, &args.init_info, - &core, ) } @@ -201,13 +199,12 @@ fn process_add_external_plugin_adapter<'a, T: DataBlob + SolanaAccount>( payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, init_info: &ExternalPluginAdapterInitInfo, - core: &T, ) -> ProgramResult { - let (_, mut plugin_header, mut plugin_registry) = + let (_, header_offset, mut plugin_header, mut plugin_registry) = create_meta_idempotent::(account, payer, system_program)?; initialize_external_plugin_adapter::( init_info, - Some(core), + header_offset, &mut plugin_header, &mut plugin_registry, account, diff --git a/programs/mpl-core/src/processor/add_plugin.rs b/programs/mpl-core/src/processor/add_plugin.rs index 77c1626b..cff3a3b3 100644 --- a/programs/mpl-core/src/processor/add_plugin.rs +++ b/programs/mpl-core/src/processor/add_plugin.rs @@ -183,11 +183,12 @@ fn process_add_plugin<'a, T: DataBlob + SolanaAccount>( plugin: &Plugin, authority: &Authority, ) -> ProgramResult { - let (_, mut plugin_header, mut plugin_registry) = + let (_, header_offset, mut plugin_header, mut plugin_registry) = create_meta_idempotent::(account, payer, system_program)?; initialize_plugin::( plugin, authority, + header_offset, &mut plugin_header, &mut plugin_registry, account, diff --git a/programs/mpl-core/src/processor/create.rs b/programs/mpl-core/src/processor/create.rs index b86ccb08..bc14f00f 100644 --- a/programs/mpl-core/src/processor/create.rs +++ b/programs/mpl-core/src/processor/create.rs @@ -173,12 +173,13 @@ pub(crate) fn process_create<'a>( if let Some(plugins) = args.plugins { if !plugins.is_empty() { - let (mut plugin_header, mut plugin_registry) = create_plugin_meta::( - &new_asset, - ctx.accounts.asset, - ctx.accounts.payer, - ctx.accounts.system_program, - )?; + let (header_offset, mut plugin_header, mut plugin_registry) = + create_plugin_meta::( + &new_asset, + ctx.accounts.asset, + ctx.accounts.payer, + ctx.accounts.system_program, + )?; for plugin in &plugins { // TODO move into plugin validation when asset/collection is part of validation context let plugin_type = PluginType::from(&plugin.plugin); @@ -209,6 +210,7 @@ pub(crate) fn process_create<'a>( initialize_plugin::( &plugin.plugin, &plugin.authority.unwrap_or(plugin.plugin.manager()), + header_offset, &mut plugin_header, &mut plugin_registry, ctx.accounts.asset, @@ -221,11 +223,12 @@ pub(crate) fn process_create<'a>( if let Some(plugins) = args.external_plugin_adapters { if !plugins.is_empty() { - let (_, mut plugin_header, mut plugin_registry) = create_meta_idempotent::( - ctx.accounts.asset, - ctx.accounts.payer, - ctx.accounts.system_program, - )?; + let (_, header_offset, mut plugin_header, mut plugin_registry) = + create_meta_idempotent::( + ctx.accounts.asset, + ctx.accounts.payer, + ctx.accounts.system_program, + )?; for plugin_init_info in &plugins { let external_check_result_bits = ExternalCheckResultBits::from( ExternalPluginAdapter::check_create(plugin_init_info), @@ -267,7 +270,7 @@ pub(crate) fn process_create<'a>( } initialize_external_plugin_adapter::( plugin_init_info, - Some(&new_asset), + header_offset, &mut plugin_header, &mut plugin_registry, ctx.accounts.asset, diff --git a/programs/mpl-core/src/processor/create_collection.rs b/programs/mpl-core/src/processor/create_collection.rs index 33d9a732..2a14cfb4 100644 --- a/programs/mpl-core/src/processor/create_collection.rs +++ b/programs/mpl-core/src/processor/create_collection.rs @@ -113,12 +113,13 @@ pub(crate) fn process_create_collection<'a>( let mut force_approved = false; if let Some(plugins) = args.plugins { if !plugins.is_empty() { - let (mut plugin_header, mut plugin_registry) = create_plugin_meta::( - &new_collection, - ctx.accounts.collection, - ctx.accounts.payer, - ctx.accounts.system_program, - )?; + let (header_offset, mut plugin_header, mut plugin_registry) = + create_plugin_meta::( + &new_collection, + ctx.accounts.collection, + ctx.accounts.payer, + ctx.accounts.system_program, + )?; for plugin in &plugins { // Cannot have owner-managed plugins on collection. if plugin.plugin.manager() == Authority::Owner { @@ -153,6 +154,7 @@ pub(crate) fn process_create_collection<'a>( initialize_plugin::( &plugin.plugin, &plugin.authority.unwrap_or(plugin.plugin.manager()), + header_offset, &mut plugin_header, &mut plugin_registry, ctx.accounts.collection, @@ -165,11 +167,12 @@ pub(crate) fn process_create_collection<'a>( if let Some(plugins) = args.external_plugin_adapters { if !plugins.is_empty() { - let (_, mut plugin_header, mut plugin_registry) = create_meta_idempotent::( - ctx.accounts.collection, - ctx.accounts.payer, - ctx.accounts.system_program, - )?; + let (_, header_offset, mut plugin_header, mut plugin_registry) = + create_meta_idempotent::( + ctx.accounts.collection, + ctx.accounts.payer, + ctx.accounts.system_program, + )?; for plugin_init_info in &plugins { if let ExternalPluginAdapterInitInfo::DataSection(_) = plugin_init_info { return Err(MplCoreError::CannotAddDataSection.into()); @@ -177,7 +180,7 @@ pub(crate) fn process_create_collection<'a>( initialize_external_plugin_adapter::( plugin_init_info, - Some(&new_collection), + header_offset, &mut plugin_header, &mut plugin_registry, ctx.accounts.collection, diff --git a/programs/mpl-core/src/processor/write_external_plugin_adapter_data.rs b/programs/mpl-core/src/processor/write_external_plugin_adapter_data.rs index ac4060d7..46026553 100644 --- a/programs/mpl-core/src/processor/write_external_plugin_adapter_data.rs +++ b/programs/mpl-core/src/processor/write_external_plugin_adapter_data.rs @@ -215,7 +215,7 @@ fn process_write_external_plugin_data<'a, T: DataBlob + SolanaAccount>( } } ExternalPluginAdapter::LinkedAppData(app_data) => { - let (_, mut header, mut registry) = + let (_, header_offset, mut header, mut registry) = create_meta_idempotent::(account, payer, system_program)?; match fetch_wrapped_external_plugin_adapter( @@ -261,7 +261,7 @@ fn process_write_external_plugin_data<'a, T: DataBlob + SolanaAccount>( parent_key: LinkedDataKey::LinkedAppData(app_data.data_authority), schema: app_data.schema, }), - Some(core), + header_offset, &mut header, &mut registry, account, @@ -274,7 +274,7 @@ fn process_write_external_plugin_data<'a, T: DataBlob + SolanaAccount>( parent_key: LinkedDataKey::LinkedAppData(app_data.data_authority), schema: app_data.schema, }), - Some(core), + header_offset, &mut header, &mut registry, account, diff --git a/programs/mpl-core/src/state/mod.rs b/programs/mpl-core/src/state/mod.rs index bd824b36..26548c62 100644 --- a/programs/mpl-core/src/state/mod.rs +++ b/programs/mpl-core/src/state/mod.rs @@ -56,6 +56,20 @@ pub enum Authority { }, } +impl DataBlob for Authority { + fn get_initial_size() -> usize { + 1 + } + + fn get_size(&self) -> usize { + 1 + if let Authority::Address { .. } = self { + 32 + } else { + 0 + } + } +} + /// An enum representing account discriminators. #[derive( Clone, Copy, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, ToPrimitive, FromPrimitive, diff --git a/programs/mpl-core/src/utils/compression.rs b/programs/mpl-core/src/utils/compression.rs index 851ab249..09e897db 100644 --- a/programs/mpl-core/src/utils/compression.rs +++ b/programs/mpl-core/src/utils/compression.rs @@ -36,13 +36,14 @@ pub(crate) fn rebuild_account_state_from_proof_data<'a>( // Add the plugins. if !plugins.is_empty() { - let (_, mut plugin_header, mut plugin_registry) = + let (_, header_offset, mut plugin_header, mut plugin_registry) = create_meta_idempotent::(asset_info, payer, system_program)?; for plugin in plugins { initialize_plugin::( &plugin.plugin, &plugin.authority, + header_offset, &mut plugin_header, &mut plugin_registry, asset_info, diff --git a/programs/mpl-core/src/utils/mod.rs b/programs/mpl-core/src/utils/mod.rs index 96fbf998..8c1931e2 100644 --- a/programs/mpl-core/src/utils/mod.rs +++ b/programs/mpl-core/src/utils/mod.rs @@ -1,5 +1,6 @@ mod account; mod compression; +mod dynamic_account_info; pub(crate) use account::*; pub(crate) use compression::*; From 33288ba60e5274a019168c772bac19223223e722 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 25 Nov 2024 14:45:01 -0500 Subject: [PATCH 02/10] Removing WIP line. --- programs/mpl-core/src/utils/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/mpl-core/src/utils/mod.rs b/programs/mpl-core/src/utils/mod.rs index 8c1931e2..96fbf998 100644 --- a/programs/mpl-core/src/utils/mod.rs +++ b/programs/mpl-core/src/utils/mod.rs @@ -1,6 +1,5 @@ mod account; mod compression; -mod dynamic_account_info; pub(crate) use account::*; pub(crate) use compression::*; From 84758e94e2a5df31e16dc1ae5b51714e7864c5dc Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 6 Dec 2024 12:07:04 -0500 Subject: [PATCH 03/10] Adding job summary. --- .github/workflows/benchmark.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index aaa9a3d6..e653b750 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -67,3 +67,5 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} # Push and deploy GitHub pages branch automatically auto-push: true + # Enable Job Summary for PRs + summary-always: true From 5243ba09239038feb398be3a64cc083ef4ef93ff Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 6 Dec 2024 12:21:01 -0500 Subject: [PATCH 04/10] Revert change. --- .github/workflows/benchmark.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index aaa9a3d6..e653b750 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -67,3 +67,5 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} # Push and deploy GitHub pages branch automatically auto-push: true + # Enable Job Summary for PRs + summary-always: true From d8aae303ec1f5ce1e40729a5ad67145747e39603 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 6 Dec 2024 12:23:40 -0500 Subject: [PATCH 05/10] Revert "Adding job summary." This reverts commit 84758e94e2a5df31e16dc1ae5b51714e7864c5dc. --- .github/workflows/benchmark.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index e653b750..aaa9a3d6 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -67,5 +67,3 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} # Push and deploy GitHub pages branch automatically auto-push: true - # Enable Job Summary for PRs - summary-always: true From 55f12f6c8f58b7c481828bd9ddfda173e7ad10e5 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Fri, 6 Dec 2024 16:30:16 -0500 Subject: [PATCH 06/10] Minor refactor to change name and make base size a constant. --- .../src/plugins/external_plugin_adapters.rs | 18 +++++- .../internal/authority_managed/add_blocker.rs | 8 +-- .../internal/authority_managed/attributes.rs | 27 ++++---- .../authority_managed/immutable_metadata.rs | 6 +- .../authority_managed/master_edition.rs | 13 ++-- .../internal/authority_managed/royalties.rs | 41 ++++++------ .../authority_managed/update_delegate.rs | 8 +-- .../authority_managed/verified_creators.rs | 17 +++-- .../internal/owner_managed/autograph.rs | 20 +++--- .../internal/owner_managed/burn_delegate.rs | 6 +- .../internal/owner_managed/freeze_delegate.rs | 8 +-- .../owner_managed/transfer_delegate.rs | 8 +-- .../src/plugins/internal/permanent/edition.rs | 8 +-- .../permanent/permanent_burn_delegate.rs | 8 +-- .../permanent/permanent_freeze_delegate.rs | 8 +-- .../permanent/permanent_transfer_delegate.rs | 8 +-- programs/mpl-core/src/plugins/lifecycle.rs | 10 ++- programs/mpl-core/src/plugins/mod.rs | 61 +++++++++--------- .../mpl-core/src/plugins/plugin_header.rs | 9 ++- .../mpl-core/src/plugins/plugin_registry.rs | 63 +++++++++++++++---- programs/mpl-core/src/plugins/utils.rs | 56 ++++++++--------- programs/mpl-core/src/processor/update.rs | 10 +-- .../update_external_plugin_adapter.rs | 2 +- .../mpl-core/src/processor/update_plugin.rs | 2 +- programs/mpl-core/src/state/asset.rs | 24 ++++--- programs/mpl-core/src/state/collection.rs | 19 +++--- programs/mpl-core/src/state/hashed_asset.rs | 12 ++-- programs/mpl-core/src/state/mod.rs | 33 ++++++---- programs/mpl-core/src/state/traits.rs | 14 +++-- programs/mpl-core/src/utils/mod.rs | 4 +- 30 files changed, 287 insertions(+), 244 deletions(-) diff --git a/programs/mpl-core/src/plugins/external_plugin_adapters.rs b/programs/mpl-core/src/plugins/external_plugin_adapters.rs index 7068807d..1546cab2 100644 --- a/programs/mpl-core/src/plugins/external_plugin_adapters.rs +++ b/programs/mpl-core/src/plugins/external_plugin_adapters.rs @@ -8,7 +8,7 @@ use strum::EnumCount; use crate::{ error::MplCoreError, plugins::{approve, reject}, - state::{AssetV1, SolanaAccount}, + state::{AssetV1, DataBlob, SolanaAccount}, }; use super::{ @@ -39,6 +39,14 @@ pub enum ExternalPluginAdapterType { DataSection, } +impl DataBlob for ExternalPluginAdapterType { + const BASE_LEN: usize = 1; + + fn len(&self) -> usize { + Self::BASE_LEN + } +} + impl From<&ExternalPluginAdapterKey> for ExternalPluginAdapterType { fn from(key: &ExternalPluginAdapterKey) -> Self { match key { @@ -398,6 +406,14 @@ pub enum HookableLifecycleEvent { Update, } +impl DataBlob for HookableLifecycleEvent { + const BASE_LEN: usize = 1; + + fn len(&self) -> usize { + Self::BASE_LEN + } +} + /// Prefix used with some of the `ExtraAccounts` that are PDAs. pub const MPL_CORE_PREFIX: &str = "mpl-core"; diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs b/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs index 9a2a4671..4a1af694 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs @@ -16,12 +16,10 @@ use crate::{ pub struct AddBlocker {} impl DataBlob for AddBlocker { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { - 0 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs index b228131b..1f6c0cc4 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs @@ -12,12 +12,11 @@ pub struct Attribute { } impl DataBlob for Attribute { - fn get_initial_size() -> usize { - 4 + 4 - } + const BASE_LEN: usize = 4 // The length of the Key string + + 4; // The length of the Value string - fn get_size(&self) -> usize { - 4 + self.key.len() + 4 + self.value.len() + fn len(&self) -> usize { + Self::BASE_LEN + self.key.len() + self.value.len() } } @@ -37,15 +36,15 @@ impl Attributes { } impl DataBlob for Attributes { - fn get_initial_size() -> usize { - 4 - } - - fn get_size(&self) -> usize { - 4 + self - .attribute_list - .iter() - .fold(0, |acc, attr| acc + attr.get_size()) + const BASE_LEN: usize = 4; // The length of the attribute list + + fn len(&self) -> usize { + Self::BASE_LEN + + self + .attribute_list + .iter() + .map(|attr| attr.len()) + .sum::() } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs b/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs index 04ecb80f..17f3fe79 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs @@ -14,11 +14,9 @@ use crate::{ pub struct ImmutableMetadata {} impl DataBlob for ImmutableMetadata { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { + fn len(&self) -> usize { 0 } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs index 21a18921..f2139b9f 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs @@ -18,15 +18,14 @@ pub struct MasterEdition { impl PluginValidation for MasterEdition {} impl DataBlob for MasterEdition { - fn get_initial_size() -> usize { - 1 + 1 + 1 - } + const BASE_LEN: usize = 1 // The max_supply option + + 1 // The name option + + 1; // The uri option - fn get_size(&self) -> usize { - 1 + self.max_supply.map_or(0, |_| 4) - + 1 + fn len(&self) -> usize { + Self::BASE_LEN + + self.max_supply.map_or(0, |_| 4) + self.name.as_ref().map_or(0, |name| 4 + name.len()) - + 1 + self.uri.as_ref().map_or(0, |uri| 4 + uri.len()) } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs index 209fb227..21d95337 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs @@ -18,12 +18,11 @@ pub struct Creator { } impl DataBlob for Creator { - fn get_initial_size() -> usize { - 33 - } + const BASE_LEN: usize = 32 // The address + + 1; // The percentage - fn get_size(&self) -> usize { - 33 + fn len(&self) -> usize { + Self::BASE_LEN } } @@ -39,16 +38,15 @@ pub enum RuleSet { } impl DataBlob for RuleSet { - fn get_initial_size() -> usize { - 1 - } - - fn get_size(&self) -> usize { - 1 + match self { - RuleSet::ProgramAllowList(allow_list) => 4 + allow_list.len() * 32, - RuleSet::ProgramDenyList(deny_list) => 4 + deny_list.len() * 32, - RuleSet::None => 0, - } + const BASE_LEN: usize = 1; // The rule set discriminator + + fn len(&self) -> usize { + Self::BASE_LEN + + match self { + RuleSet::ProgramAllowList(allow_list) => 4 + allow_list.len() * 32, + RuleSet::ProgramDenyList(deny_list) => 4 + deny_list.len() * 32, + RuleSet::None => 0, + } } } @@ -64,14 +62,15 @@ pub struct Royalties { } impl DataBlob for Royalties { - fn get_initial_size() -> usize { - 1 - } + const BASE_LEN: usize = 2 // basis_points + + 4 // creators length + + RuleSet::BASE_LEN; // rule_set - fn get_size(&self) -> usize { + fn len(&self) -> usize { 2 // basis_points - + (4 + self.creators.len() * Creator::get_initial_size()) // creators - + self.rule_set.get_size() // rule_set + + 4 // creators length + + self.creators.iter().map(|creator| creator.len()).sum::() + + self.rule_set.len() // rule_set } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs index d305a00d..97ec938a 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs @@ -38,12 +38,10 @@ impl Default for UpdateDelegate { } impl DataBlob for UpdateDelegate { - fn get_initial_size() -> usize { - 4 - } + const BASE_LEN: usize = 4; // The additional delegates length - fn get_size(&self) -> usize { - 4 + self.additional_delegates.len() * 32 + fn len(&self) -> usize { + Self::BASE_LEN + self.additional_delegates.len() * 32 } } diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs index e90d7706..cc51396f 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs @@ -18,12 +18,11 @@ pub struct VerifiedCreatorsSignature { } impl DataBlob for VerifiedCreatorsSignature { - fn get_initial_size() -> usize { - 32 + 1 - } + const BASE_LEN: usize = 32 // The address + + 1; // The verified boolean - fn get_size(&self) -> usize { - 32 + 1 + fn len(&self) -> usize { + Self::BASE_LEN } } @@ -35,12 +34,10 @@ pub struct VerifiedCreators { } impl DataBlob for VerifiedCreators { - fn get_initial_size() -> usize { - 4 - } + const BASE_LEN: usize = 4; // The signatures length - fn get_size(&self) -> usize { - 4 + self.signatures.len() * VerifiedCreatorsSignature::get_initial_size() + fn len(&self) -> usize { + Self::BASE_LEN + self.signatures.iter().map(|sig| sig.len()).sum::() } } diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs index b7a923f3..4c5272b5 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs @@ -19,12 +19,11 @@ pub struct AutographSignature { } impl DataBlob for AutographSignature { - fn get_initial_size() -> usize { - 32 + 4 - } + const BASE_LEN: usize = 32 // The address + + 4; // The message length - fn get_size(&self) -> usize { - 32 + 4 + self.message.len() + fn len(&self) -> usize { + Self::BASE_LEN + self.message.len() } } @@ -135,14 +134,9 @@ impl PluginValidation for Autograph { } impl DataBlob for Autograph { - fn get_initial_size() -> usize { - 4 - } + const BASE_LEN: usize = 4; // The signatures length - fn get_size(&self) -> usize { - 4 + self - .signatures - .iter() - .fold(0, |acc, sig| acc + sig.get_size()) + fn len(&self) -> usize { + Self::BASE_LEN + self.signatures.iter().map(|sig| sig.len()).sum::() } } diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs index 15075fb3..ff9843c8 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs @@ -26,11 +26,9 @@ impl Default for BurnDelegate { } impl DataBlob for BurnDelegate { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { + fn len(&self) -> usize { 0 } } diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs index e9e2574f..87a842e2 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs @@ -32,12 +32,10 @@ impl Default for FreezeDelegate { } impl DataBlob for FreezeDelegate { - fn get_initial_size() -> usize { - 1 - } + const BASE_LEN: usize = 1; // The frozen boolean - fn get_size(&self) -> usize { - 1 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs index 0ef04138..7da1ccc4 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs @@ -27,12 +27,10 @@ impl Default for TransferDelegate { } impl DataBlob for TransferDelegate { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { - 0 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/permanent/edition.rs b/programs/mpl-core/src/plugins/internal/permanent/edition.rs index 7ea3b475..b71cf533 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/edition.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/edition.rs @@ -48,11 +48,9 @@ impl PluginValidation for Edition { } impl DataBlob for Edition { - fn get_initial_size() -> usize { - 4 - } + const BASE_LEN: usize = 4; // The edition number - fn get_size(&self) -> usize { - 4 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs index 9717ffdd..3515c66a 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs @@ -16,12 +16,10 @@ use crate::{ pub struct PermanentBurnDelegate {} impl DataBlob for PermanentBurnDelegate { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { - 0 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs index 04b3daf7..d6cba7dc 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs @@ -31,12 +31,10 @@ impl Default for PermanentFreezeDelegate { } impl DataBlob for PermanentFreezeDelegate { - fn get_initial_size() -> usize { - 1 - } + const BASE_LEN: usize = 1; // The frozen boolean - fn get_size(&self) -> usize { - 1 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs index e7435595..147c5cd7 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs @@ -15,12 +15,10 @@ use crate::plugins::{ pub struct PermanentTransferDelegate {} impl DataBlob for PermanentTransferDelegate { - fn get_initial_size() -> usize { - 0 - } + const BASE_LEN: usize = 0; - fn get_size(&self) -> usize { - 0 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 2f47c5ed..c3f758d4 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, UpdateAuthority}, + state::{Authority, DataBlob, Key, UpdateAuthority}, }; use super::{ @@ -37,6 +37,14 @@ pub struct ExternalCheckResult { pub flags: u32, } +impl DataBlob for ExternalCheckResult { + const BASE_LEN: usize = 4; // u32 flags + + fn len(&self) -> usize { + Self::BASE_LEN + } +} + impl ExternalCheckResult { pub(crate) fn none() -> Self { Self { flags: 0 } diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index c93f9820..6b72d4ef 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -89,34 +89,33 @@ impl Plugin { impl Compressible for Plugin {} impl DataBlob for Plugin { - fn get_initial_size() -> usize { - 1 - } - - fn get_size(&self) -> usize { - 1 + match self { - Plugin::Royalties(royalties) => royalties.get_size(), - Plugin::FreezeDelegate(freeze_delegate) => freeze_delegate.get_size(), - Plugin::BurnDelegate(burn_delegate) => burn_delegate.get_size(), - Plugin::TransferDelegate(transfer_delegate) => transfer_delegate.get_size(), - Plugin::UpdateDelegate(update_delegate) => update_delegate.get_size(), - Plugin::PermanentFreezeDelegate(permanent_freeze_delegate) => { - permanent_freeze_delegate.get_size() - } - Plugin::Attributes(attributes) => attributes.get_size(), - Plugin::PermanentTransferDelegate(permanent_transfer_delegate) => { - permanent_transfer_delegate.get_size() + const BASE_LEN: usize = 1; + + fn len(&self) -> usize { + Self::BASE_LEN // The discriminator + + match self { + Plugin::Royalties(royalties) => royalties.len(), + Plugin::FreezeDelegate(freeze_delegate) => freeze_delegate.len(), + Plugin::BurnDelegate(burn_delegate) => burn_delegate.len(), + Plugin::TransferDelegate(transfer_delegate) => transfer_delegate.len(), + Plugin::UpdateDelegate(update_delegate) => update_delegate.len(), + Plugin::PermanentFreezeDelegate(permanent_freeze_delegate) => { + permanent_freeze_delegate.len() + } + Plugin::Attributes(attributes) => attributes.len(), + Plugin::PermanentTransferDelegate(permanent_transfer_delegate) => { + permanent_transfer_delegate.len() + } + Plugin::PermanentBurnDelegate(permanent_burn_delegate) => { + permanent_burn_delegate.len() + } + Plugin::Edition(edition) => edition.len(), + Plugin::MasterEdition(master_edition) => master_edition.len(), + Plugin::AddBlocker(add_blocker) => add_blocker.len(), + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata.len(), + Plugin::VerifiedCreators(verified_creators) => verified_creators.len(), + Plugin::Autograph(autograph) => autograph.len(), } - Plugin::PermanentBurnDelegate(permanent_burn_delegate) => { - permanent_burn_delegate.get_size() - } - Plugin::Edition(edition) => edition.get_size(), - Plugin::MasterEdition(master_edition) => master_edition.get_size(), - Plugin::AddBlocker(add_blocker) => add_blocker.get_size(), - Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata.get_size(), - Plugin::VerifiedCreators(verified_creators) => verified_creators.get_size(), - Plugin::Autograph(autograph) => autograph.get_size(), - } } } @@ -169,12 +168,10 @@ pub enum PluginType { } impl DataBlob for PluginType { - fn get_initial_size() -> usize { - 1 - } + const BASE_LEN: usize = 1; - fn get_size(&self) -> usize { - 1 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/plugin_header.rs b/programs/mpl-core/src/plugins/plugin_header.rs index 366feda3..ec5bb107 100644 --- a/programs/mpl-core/src/plugins/plugin_header.rs +++ b/programs/mpl-core/src/plugins/plugin_header.rs @@ -15,12 +15,11 @@ pub struct PluginHeaderV1 { } impl DataBlob for PluginHeaderV1 { - fn get_initial_size() -> usize { - 1 + 8 - } + const BASE_LEN: usize = 1 // Key + + 8; // Offset - fn get_size(&self) -> usize { - 1 + 8 + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/plugins/plugin_registry.rs b/programs/mpl-core/src/plugins/plugin_registry.rs index 134069f3..b21b30e7 100644 --- a/programs/mpl-core/src/plugins/plugin_registry.rs +++ b/programs/mpl-core/src/plugins/plugin_registry.rs @@ -112,12 +112,22 @@ impl PluginRegistryV1 { } impl DataBlob for PluginRegistryV1 { - fn get_initial_size() -> usize { - 9 - } - - fn get_size(&self) -> usize { - 9 //TODO: Fix this + const BASE_LEN: usize = 1 // Key + + 4 // Registry Length + + 4; // External Registry Length + + fn len(&self) -> usize { + Self::BASE_LEN + + self + .registry + .iter() + .map(|record| record.len()) + .sum::() + + self + .external_registry + .iter() + .map(|record| record.len()) + .sum::() } } @@ -132,7 +142,7 @@ impl SolanaAccount for PluginRegistryV1 { #[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] pub struct RegistryRecord { /// The type of plugin. - pub plugin_type: PluginType, // 2 + pub plugin_type: PluginType, // 1 /// The authority who has permission to utilize a plugin. pub authority: Authority, // Variable /// The offset to the plugin in the account. @@ -147,12 +157,12 @@ impl RegistryRecord { } impl DataBlob for RegistryRecord { - fn get_initial_size() -> usize { - 2 + 1 + 8 - } + const BASE_LEN: usize = PluginType::BASE_LEN + + Authority::BASE_LEN // Authority Discriminator + + 8; // Offset - fn get_size(&self) -> usize { - 2 + self.authority.get_size() + 8 + fn len(&self) -> usize { + self.plugin_type.len() + self.authority.len() + 8 } } @@ -198,3 +208,32 @@ impl ExternalRegistryRecord { Ok(()) } } + +impl DataBlob for ExternalRegistryRecord { + const BASE_LEN: usize = ExternalPluginAdapterType::BASE_LEN + + Authority::BASE_LEN // Authority Discriminator + + 1 // Lifecycle checks option + + 8 // Offset + + 1 // Data offset option + + 1; // Data len option + + fn len(&self) -> usize { + let mut len = self.plugin_type.len() + self.authority.len() + 1 + 8 + 1 + 1; + + if let Some(checks) = &self.lifecycle_checks { + len += 4 // 4 bytes for the length of the checks vector + + checks.len() + * (HookableLifecycleEvent::BASE_LEN + ExternalCheckResult::BASE_LEN); + } + + if self.data_offset.is_some() { + len += 8; + } + + if self.data_len.is_some() { + len += 8; + } + + len + } +} diff --git a/programs/mpl-core/src/plugins/utils.rs b/programs/mpl-core/src/plugins/utils.rs index bf2fdb7b..1fef2284 100644 --- a/programs/mpl-core/src/plugins/utils.rs +++ b/programs/mpl-core/src/plugins/utils.rs @@ -29,14 +29,14 @@ pub fn create_meta_idempotent<'a, T: SolanaAccount + DataBlob>( system_program: &AccountInfo<'a>, ) -> Result<(T, usize, PluginHeaderV1, PluginRegistryV1), ProgramError> { let core = T::load(account, 0)?; - let header_offset = core.get_size(); + let header_offset = core.len(); // Check if the plugin header and registry exist. if header_offset == account.data_len() { // They don't exist, so create them. let header = PluginHeaderV1 { key: Key::PluginHeaderV1, - plugin_registry_offset: header_offset + PluginHeaderV1::get_initial_size(), + plugin_registry_offset: header_offset + PluginHeaderV1::BASE_LEN, }; let registry = PluginRegistryV1 { key: Key::PluginRegistryV1, @@ -48,7 +48,7 @@ pub fn create_meta_idempotent<'a, T: SolanaAccount + DataBlob>( account, payer, system_program, - header.plugin_registry_offset + PluginRegistryV1::get_initial_size(), + header.plugin_registry_offset + PluginRegistryV1::BASE_LEN, )?; header.save(account, header_offset)?; @@ -71,12 +71,12 @@ pub fn create_plugin_meta<'a, T: SolanaAccount + DataBlob>( payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, ) -> Result<(usize, PluginHeaderV1, PluginRegistryV1), ProgramError> { - let header_offset = asset.get_size(); + let header_offset = asset.len(); // They don't exist, so create them. let header = PluginHeaderV1 { key: Key::PluginHeaderV1, - plugin_registry_offset: header_offset + PluginHeaderV1::get_initial_size(), + plugin_registry_offset: header_offset + PluginHeaderV1::BASE_LEN, }; let registry = PluginRegistryV1 { key: Key::PluginRegistryV1, @@ -88,7 +88,7 @@ pub fn create_plugin_meta<'a, T: SolanaAccount + DataBlob>( account, payer, system_program, - header.plugin_registry_offset + PluginRegistryV1::get_initial_size(), + header.plugin_registry_offset + PluginRegistryV1::BASE_LEN, )?; header.save(account, header_offset)?; @@ -102,7 +102,7 @@ pub fn assert_plugins_initialized(account: &AccountInfo) -> ProgramResult { let mut bytes: &[u8] = &(*account.data).borrow(); let asset = AssetV1::deserialize(&mut bytes)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginsNotInitialized.into()); } @@ -116,11 +116,11 @@ pub fn fetch_plugin( ) -> Result<(Authority, U, usize), ProgramError> { let asset = T::load(account, 0)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginNotFound.into()); } - let header = PluginHeaderV1::load(account, asset.get_size())?; + let header = PluginHeaderV1::load(account, asset.len())?; let PluginRegistryV1 { registry, .. } = PluginRegistryV1::load(account, header.plugin_registry_offset)?; @@ -155,15 +155,15 @@ pub fn fetch_wrapped_plugin( plugin_type: PluginType, ) -> Result<(Authority, Plugin), ProgramError> { let size = match core { - Some(core) => core.get_size(), + Some(core) => core.len(), None => { let asset = T::load(account, 0)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginNotFound.into()); } - asset.get_size() + asset.len() } }; @@ -191,15 +191,15 @@ pub fn fetch_wrapped_external_plugin_adapter( plugin_key: &ExternalPluginAdapterKey, ) -> Result<(ExternalRegistryRecord, ExternalPluginAdapter), ProgramError> { let size = match core { - Some(core) => core.get_size(), + Some(core) => core.len(), None => { let asset = T::load(account, 0)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::ExternalPluginAdapterNotFound.into()); } - asset.get_size() + asset.len() } }; @@ -225,11 +225,11 @@ pub fn fetch_wrapped_external_plugin_adapter( pub fn fetch_plugins(account: &AccountInfo) -> Result, ProgramError> { let asset = AssetV1::load(account, 0)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginNotFound.into()); } - let header = PluginHeaderV1::load(account, asset.get_size())?; + let header = PluginHeaderV1::load(account, asset.len())?; let PluginRegistryV1 { registry, .. } = PluginRegistryV1::load(account, header.plugin_registry_offset)?; @@ -240,11 +240,11 @@ pub fn fetch_plugins(account: &AccountInfo) -> Result, Progr pub fn list_plugins(account: &AccountInfo) -> Result, ProgramError> { let asset = AssetV1::load(account, 0)?; - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginNotFound.into()); } - let header = PluginHeaderV1::load(account, asset.get_size())?; + let header = PluginHeaderV1::load(account, asset.len())?; let PluginRegistryV1 { registry, .. } = PluginRegistryV1::load(account, header.plugin_registry_offset)?; @@ -267,7 +267,7 @@ pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>( system_program: &AccountInfo<'a>, ) -> ProgramResult { let plugin_type = plugin.into(); - let plugin_size = plugin.get_size(); + let plugin_size = plugin.len(); // You cannot add a duplicate plugin. if plugin_registry @@ -287,7 +287,7 @@ pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>( }; let size_increase = plugin_size - .checked_add(new_registry_record.get_size()) + .checked_add(new_registry_record.len()) .ok_or(MplCoreError::NumericalOverflow)?; let new_registry_offset = plugin_header @@ -521,7 +521,7 @@ pub fn update_external_plugin_adapter_data<'a, T: DataBlob + SolanaAccount>( plugin_registry.external_registry[record_index].data_len = Some(new_data_len); plugin_registry.save(account, new_registry_offset as usize)?; - plugin_header.save(account, core.map_or(0, |core| core.get_size()))?; + plugin_header.save(account, core.map_or(0, |core| core.len()))?; Ok(()) } @@ -564,12 +564,12 @@ pub fn delete_plugin<'a, T: DataBlob>( payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, ) -> ProgramResult { - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::PluginNotFound.into()); } //TODO: Bytemuck this. - let mut header = PluginHeaderV1::load(account, asset.get_size())?; + let mut header = PluginHeaderV1::load(account, asset.len())?; let mut plugin_registry = PluginRegistryV1::load(account, header.plugin_registry_offset)?; if let Some(index) = plugin_registry @@ -617,7 +617,7 @@ pub fn delete_plugin<'a, T: DataBlob>( ); header.plugin_registry_offset = new_registry_offset; - header.save(account, asset.get_size())?; + header.save(account, asset.len())?; // Move offsets for existing registry records. plugin_registry.bump_offsets(plugin_offset, -(serialized_plugin.len() as isize))?; @@ -640,12 +640,12 @@ pub fn delete_external_plugin_adapter<'a, T: DataBlob>( payer: &AccountInfo<'a>, system_program: &AccountInfo<'a>, ) -> ProgramResult { - if asset.get_size() == account.data_len() { + if asset.len() == account.data_len() { return Err(MplCoreError::ExternalPluginAdapterNotFound.into()); } //TODO: Bytemuck this. - let mut header = PluginHeaderV1::load(account, asset.get_size())?; + let mut header = PluginHeaderV1::load(account, asset.len())?; let mut plugin_registry = PluginRegistryV1::load(account, header.plugin_registry_offset)?; let result = find_external_plugin_adapter(&plugin_registry, plugin_key, account)?; @@ -695,7 +695,7 @@ pub fn delete_external_plugin_adapter<'a, T: DataBlob>( ); header.plugin_registry_offset = new_registry_offset; - header.save(account, asset.get_size())?; + header.save(account, asset.len())?; // Move offsets for existing registry records. plugin_registry.bump_offsets(plugin_offset, -(serialized_plugin_len as isize))?; diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index 23c6d9db..e3c255dd 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -128,7 +128,7 @@ fn update<'a>( // 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.len() as isize; let mut dirty = false; if let Some(new_update_authority) = args.new_update_authority { @@ -283,7 +283,7 @@ pub(crate) fn update_collection<'a>( Some(HookableLifecycleEvent::Update), )?; - let collection_size = collection.get_size() as isize; + let collection_size = collection.len() as isize; let mut dirty = false; if let Some(new_update_authority) = ctx.accounts.new_update_authority { @@ -326,7 +326,7 @@ 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.len() as isize; // 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 @@ -347,7 +347,7 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( // 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.len() as isize) .ok_or(MplCoreError::NumericalOverflow)?; let new_plugin_offset = plugin_offset @@ -372,7 +372,7 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( plugin_registry.save(account, new_registry_offset as usize)?; } else { - resize_or_reallocate_account(account, payer, system_program, core.get_size())?; + resize_or_reallocate_account(account, payer, system_program, core.len())?; } core.save(account, 0)?; diff --git a/programs/mpl-core/src/processor/update_external_plugin_adapter.rs b/programs/mpl-core/src/processor/update_external_plugin_adapter.rs index ed6d8622..a0398d9e 100644 --- a/programs/mpl-core/src/processor/update_external_plugin_adapter.rs +++ b/programs/mpl-core/src/processor/update_external_plugin_adapter.rs @@ -250,7 +250,7 @@ fn process_update_external_plugin_adapter<'a, T: DataBlob + SolanaAccount>( src.len(), ); - plugin_header.save(account, core.get_size())?; + plugin_header.save(account, core.len())?; // Move offsets for existing registry records. plugin_registry.bump_offsets(registry_record.offset, plugin_size_diff)?; diff --git a/programs/mpl-core/src/processor/update_plugin.rs b/programs/mpl-core/src/processor/update_plugin.rs index a4810f36..64fc9adf 100644 --- a/programs/mpl-core/src/processor/update_plugin.rs +++ b/programs/mpl-core/src/processor/update_plugin.rs @@ -196,7 +196,7 @@ fn process_update_plugin<'a, T: DataBlob + SolanaAccount>( src.len(), ); - plugin_header.save(account, core.get_size())?; + plugin_header.save(account, core.len())?; plugin_registry.bump_offsets(registry_record.offset, size_diff)?; diff --git a/programs/mpl-core/src/state/asset.rs b/programs/mpl-core/src/state/asset.rs index 30ec3c56..5cbfccdc 100644 --- a/programs/mpl-core/src/state/asset.rs +++ b/programs/mpl-core/src/state/asset.rs @@ -21,7 +21,6 @@ pub struct AssetV1 { pub key: Key, //1 /// The owner of the asset. pub owner: Pubkey, //32 - //TODO: Fix this for dynamic size /// The update authority of the asset. pub update_authority: UpdateAuthority, //33 /// The name of the asset. @@ -60,9 +59,6 @@ impl AssetV1 { Ok(()) } - /// The base length of the asset account with an empty name and uri and no seq. - pub const BASE_LENGTH: usize = 1 + 32 + 33 + 4 + 4 + 1; - /// Check permissions for the create lifecycle event. pub fn check_create() -> CheckResult { CheckResult::CanApprove @@ -358,12 +354,22 @@ impl AssetV1 { impl Compressible for AssetV1 {} impl DataBlob for AssetV1 { - fn get_initial_size() -> usize { - AssetV1::BASE_LENGTH - } + /// The base length of the asset account with an empty name and uri and no seq. + const BASE_LEN: usize = 1 // Key + + 32 // Owner + + 1 // Update Authority discriminator + + 4 // Name length + + 4 // URI length + + 1; // Seq option + + fn len(&self) -> usize { + let mut size = AssetV1::BASE_LEN + self.name.len() + self.uri.len(); + + if let UpdateAuthority::Address(_) | UpdateAuthority::Collection(_) = self.update_authority + { + size += 32; + } - fn get_size(&self) -> usize { - let mut size = AssetV1::BASE_LENGTH + self.name.len() + self.uri.len(); if self.seq.is_some() { size += size_of::(); } diff --git a/programs/mpl-core/src/state/collection.rs b/programs/mpl-core/src/state/collection.rs index f59fd388..78da0bea 100644 --- a/programs/mpl-core/src/state/collection.rs +++ b/programs/mpl-core/src/state/collection.rs @@ -27,9 +27,6 @@ pub struct CollectionV1 { } impl CollectionV1 { - /// The base length of the collection account with an empty name and uri. - pub const BASE_LENGTH: usize = 1 + 32 + 4 + 4 + 4 + 4; - /// Create a new collection. pub fn new( update_authority: Pubkey, @@ -351,12 +348,16 @@ impl CollectionV1 { } impl DataBlob for CollectionV1 { - fn get_initial_size() -> usize { - Self::BASE_LENGTH - } - - fn get_size(&self) -> usize { - Self::BASE_LENGTH + self.name.len() + self.uri.len() + /// The base length of the collection account with an empty name and uri. + const BASE_LEN: usize = 1 // Key + + 32 // Update Authority + + 4 // Name Length + + 4 // URI Length + + 4 // num_minted + + 4; // current_size + + fn len(&self) -> usize { + Self::BASE_LEN + self.name.len() + self.uri.len() } } diff --git a/programs/mpl-core/src/state/hashed_asset.rs b/programs/mpl-core/src/state/hashed_asset.rs index 647062c0..279d05e6 100644 --- a/programs/mpl-core/src/state/hashed_asset.rs +++ b/programs/mpl-core/src/state/hashed_asset.rs @@ -13,9 +13,6 @@ pub struct HashedAssetV1 { } impl HashedAssetV1 { - /// The length of the hashed asset account. - pub const LENGTH: usize = 1 + 32; - /// Create a new hashed asset. pub fn new(hash: [u8; 32]) -> Self { Self { @@ -26,12 +23,11 @@ impl HashedAssetV1 { } impl DataBlob for HashedAssetV1 { - fn get_initial_size() -> usize { - HashedAssetV1::LENGTH - } + const BASE_LEN: usize = 1 // The Key + + 32; // The hash - fn get_size(&self) -> usize { - HashedAssetV1::LENGTH + fn len(&self) -> usize { + Self::BASE_LEN } } diff --git a/programs/mpl-core/src/state/mod.rs b/programs/mpl-core/src/state/mod.rs index 26548c62..9d1e6872 100644 --- a/programs/mpl-core/src/state/mod.rs +++ b/programs/mpl-core/src/state/mod.rs @@ -57,16 +57,15 @@ pub enum Authority { } impl DataBlob for Authority { - fn get_initial_size() -> usize { - 1 - } + const BASE_LEN: usize = 1; // 1 byte for the discriminator - fn get_size(&self) -> usize { - 1 + if let Authority::Address { .. } = self { - 32 - } else { - 0 - } + fn len(&self) -> usize { + Self::BASE_LEN + + if let Authority::Address { .. } = self { + 32 + } else { + 0 + } } } @@ -89,9 +88,17 @@ pub enum Key { CollectionV1, } -impl Key { - /// Get the size of the Key. - pub fn get_initial_size() -> usize { - 1 +impl DataBlob for Key { + const BASE_LEN: usize = 1; // 1 byte for the discriminator + + fn len(&self) -> usize { + Self::BASE_LEN } } + +// impl Key { +// /// Get the size of the Key. +// pub fn get_initial_size() -> usize { +// 1 +// } +// } diff --git a/programs/mpl-core/src/state/traits.rs b/programs/mpl-core/src/state/traits.rs index 008b6c45..9c656c78 100644 --- a/programs/mpl-core/src/state/traits.rs +++ b/programs/mpl-core/src/state/traits.rs @@ -9,10 +9,16 @@ use super::UpdateAuthority; /// A trait for generic blobs of data that have size. pub trait DataBlob: BorshSerialize + BorshDeserialize { - /// Get the size of an empty instance of the data blob. - fn get_initial_size() -> usize; - /// Get the current size of the data blob. - fn get_size(&self) -> usize; + /// The base length of the data blob. + const BASE_LEN: usize; + + /// Get the current length of the data blob. + fn len(&self) -> usize; + + /// Check if the data blob is empty. + fn is_empty(&self) -> bool { + self.len() == Self::BASE_LEN + } } /// A trait for Solana accounts. diff --git a/programs/mpl-core/src/utils/mod.rs b/programs/mpl-core/src/utils/mod.rs index 96fbf998..5438007f 100644 --- a/programs/mpl-core/src/utils/mod.rs +++ b/programs/mpl-core/src/utils/mod.rs @@ -89,8 +89,8 @@ pub fn fetch_core_data( ) -> Result<(T, Option, Option), ProgramError> { let asset = T::load(account, 0)?; - if asset.get_size() != account.data_len() { - let plugin_header = PluginHeaderV1::load(account, asset.get_size())?; + if asset.len() != account.data_len() { + let plugin_header = PluginHeaderV1::load(account, asset.len())?; let plugin_registry = PluginRegistryV1::load(account, plugin_header.plugin_registry_offset)?; From 146c59354bf54df6c9d7effc8e51b9144dafcc8d Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 9 Dec 2024 17:03:51 -0500 Subject: [PATCH 07/10] Adding program tests. --- .../src/plugins/external_plugin_adapters.rs | 52 ++++- .../internal/authority_managed/add_blocker.rs | 12 ++ .../internal/authority_managed/attributes.rs | 40 ++++ .../authority_managed/immutable_metadata.rs | 12 ++ .../authority_managed/master_edition.rs | 23 +++ .../internal/authority_managed/royalties.rs | 101 ++++++++- .../authority_managed/update_delegate.rs | 28 +++ .../authority_managed/verified_creators.rs | 48 ++++- .../internal/owner_managed/autograph.rs | 48 ++++- .../internal/owner_managed/burn_delegate.rs | 12 ++ .../internal/owner_managed/freeze_delegate.rs | 12 ++ .../owner_managed/transfer_delegate.rs | 12 ++ .../permanent/permanent_burn_delegate.rs | 12 ++ .../permanent/permanent_freeze_delegate.rs | 12 ++ .../permanent/permanent_transfer_delegate.rs | 12 ++ programs/mpl-core/src/plugins/lifecycle.rs | 26 ++- programs/mpl-core/src/plugins/mod.rs | 191 +++++++++++++++++- .../mpl-core/src/plugins/plugin_header.rs | 15 ++ .../mpl-core/src/plugins/plugin_registry.rs | 145 +++++++++++++ programs/mpl-core/src/state/asset.rs | 39 ++++ programs/mpl-core/src/state/collection.rs | 31 +++ programs/mpl-core/src/state/hashed_asset.rs | 12 ++ programs/mpl-core/src/state/mod.rs | 57 +++++- 23 files changed, 924 insertions(+), 28 deletions(-) diff --git a/programs/mpl-core/src/plugins/external_plugin_adapters.rs b/programs/mpl-core/src/plugins/external_plugin_adapters.rs index 1546cab2..7d4d33b4 100644 --- a/programs/mpl-core/src/plugins/external_plugin_adapters.rs +++ b/programs/mpl-core/src/plugins/external_plugin_adapters.rs @@ -3,7 +3,7 @@ use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, }; -use strum::EnumCount; +use strum::{EnumCount, EnumIter}; use crate::{ error::MplCoreError, @@ -22,7 +22,17 @@ use super::{ /// List of third party plugin types. #[repr(C)] #[derive( - Clone, Copy, Debug, BorshSerialize, BorshDeserialize, Eq, PartialEq, EnumCount, PartialOrd, Ord, + Clone, + Copy, + Debug, + BorshSerialize, + BorshDeserialize, + Eq, + PartialEq, + EnumCount, + PartialOrd, + Ord, + EnumIter, )] pub enum ExternalPluginAdapterType { /// Lifecycle Hook. @@ -391,7 +401,9 @@ impl From<&ExternalPluginAdapterInitInfo> for ExternalPluginAdapter { } #[repr(C)] -#[derive(Eq, PartialEq, Clone, BorshSerialize, BorshDeserialize, Debug, PartialOrd, Ord, Hash)] +#[derive( + Eq, PartialEq, Clone, BorshSerialize, BorshDeserialize, Debug, PartialOrd, Ord, Hash, EnumIter, +)] /// An enum listing all the lifecyle events available for external plugin adapter hooks. Note that some /// lifecycle events such as adding and removing plugins will be checked by default as they are /// inherently part of the external plugin adapter system. @@ -763,3 +775,37 @@ impl From<&ExternalPluginAdapterInitInfo> for ExternalPluginAdapterKey { } } } + +/// Test DataBlob sizing +#[cfg(test)] +mod test { + use strum::IntoEnumIterator; + + use super::*; + + #[test] + fn test_external_plugin_adapter_type_size() { + for fixture in ExternalPluginAdapterType::iter() { + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } + } + + #[test] + fn test_hookable_lifecycle_event_size() { + for fixture in HookableLifecycleEvent::iter() { + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs b/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs index 4a1af694..688e8537 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/add_blocker.rs @@ -39,3 +39,15 @@ impl PluginValidation for AddBlocker { reject!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_blocker_len() { + let add_blocker = AddBlocker {}; + let serialized = add_blocker.try_to_vec().unwrap(); + assert_eq!(serialized.len(), add_blocker.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs index 1f6c0cc4..ece26e40 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/attributes.rs @@ -49,3 +49,43 @@ impl DataBlob for Attributes { } impl PluginValidation for Attributes {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_attribute_len() { + let attribute = Attribute { + key: "test".to_string(), + value: "test".to_string(), + }; + let serialized = attribute.try_to_vec().unwrap(); + assert_eq!(serialized.len(), attribute.len()); + } + + #[test] + fn test_attributes_default_len() { + let attributes = Attributes::new(); + let serialized = attributes.try_to_vec().unwrap(); + assert_eq!(serialized.len(), attributes.len()); + } + + #[test] + fn test_attributes_len() { + let attributes = Attributes { + attribute_list: vec![ + Attribute { + key: "test".to_string(), + value: "test".to_string(), + }, + Attribute { + key: "test2".to_string(), + value: "test2".to_string(), + }, + ], + }; + let serialized = attributes.try_to_vec().unwrap(); + assert_eq!(serialized.len(), attributes.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs b/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs index 17f3fe79..0f296884 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/immutable_metadata.rs @@ -30,3 +30,15 @@ impl PluginValidation for ImmutableMetadata { reject!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_immutable_metadata_len() { + let immutable_metadata = ImmutableMetadata {}; + let serialized = immutable_metadata.try_to_vec().unwrap(); + assert_eq!(serialized.len(), immutable_metadata.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs index f2139b9f..dfa6edc8 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/master_edition.rs @@ -29,3 +29,26 @@ impl DataBlob for MasterEdition { + self.uri.as_ref().map_or(0, |uri| 4 + uri.len()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_master_edition_default_len() { + let master_edition = MasterEdition::default(); + let serialized = master_edition.try_to_vec().unwrap(); + assert_eq!(serialized.len(), master_edition.len()); + } + + #[test] + fn test_master_edition_len() { + let master_edition = MasterEdition { + max_supply: Some(100), + name: Some("test".to_string()), + uri: Some("test".to_string()), + }; + let serialized = master_edition.try_to_vec().unwrap(); + assert_eq!(serialized.len(), master_edition.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs index 21d95337..4bdaec45 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs @@ -13,8 +13,10 @@ use crate::state::DataBlob; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] pub struct Creator { - address: Pubkey, // 32 - percentage: u8, // 1 + /// The address of the creator. + pub address: Pubkey, // 32 + /// The percentage of royalties to be paid to the creator. + pub percentage: u8, // 1 } impl DataBlob for Creator { @@ -54,11 +56,11 @@ impl DataBlob for RuleSet { #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct Royalties { /// The percentage of royalties to be paid to the creators. - basis_points: u16, // 2 + pub basis_points: u16, // 2 /// A list of creators to receive royalties. - creators: Vec, // 4 + pub creators: Vec, // 4 /// The rule set for the asset to enforce royalties. - rule_set: RuleSet, // 1 + pub rule_set: RuleSet, // 1 } impl DataBlob for Royalties { @@ -169,3 +171,92 @@ impl PluginValidation for Royalties { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creator_len() { + let creator = Creator { + address: Pubkey::default(), + percentage: 100, + }; + let serialized = creator.try_to_vec().unwrap(); + assert_eq!(serialized.len(), creator.len()); + } + + #[test] + fn test_rule_set_default_len() { + let rule_set = RuleSet::None; + let serialized = rule_set.try_to_vec().unwrap(); + assert_eq!(serialized.len(), rule_set.len()); + } + + #[test] + fn test_rule_set_len() { + let rule_sets = vec![ + RuleSet::ProgramAllowList(vec![Pubkey::default()]), + RuleSet::ProgramDenyList(vec![Pubkey::default(), Pubkey::default()]), + ]; + for rule_set in rule_sets { + let serialized = rule_set.try_to_vec().unwrap(); + assert_eq!(serialized.len(), rule_set.len()); + } + } + + #[test] + fn test_royalties_len() { + let royalties = vec![ + Royalties { + basis_points: 0, + creators: vec![], + rule_set: RuleSet::None, + }, + Royalties { + basis_points: 1, + creators: vec![Creator { + address: Pubkey::default(), + percentage: 1, + }], + rule_set: RuleSet::ProgramAllowList(vec![]), + }, + Royalties { + basis_points: 2, + creators: vec![ + Creator { + address: Pubkey::default(), + percentage: 2, + }, + Creator { + address: Pubkey::default(), + percentage: 3, + }, + ], + rule_set: RuleSet::ProgramDenyList(vec![Pubkey::default()]), + }, + Royalties { + basis_points: 3, + creators: vec![ + Creator { + address: Pubkey::default(), + percentage: 3, + }, + Creator { + address: Pubkey::default(), + percentage: 4, + }, + Creator { + address: Pubkey::default(), + percentage: 5, + }, + ], + rule_set: RuleSet::ProgramDenyList(vec![Pubkey::default(), Pubkey::default()]), + }, + ]; + for royalty in royalties { + let serialized = royalty.try_to_vec().unwrap(); + assert_eq!(serialized.len(), royalty.len()); + } + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs index 97ec938a..a2911174 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/update_delegate.rs @@ -209,3 +209,31 @@ impl PluginValidation for UpdateDelegate { abstain!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_update_delegate_default_len() { + let update_delegate = UpdateDelegate::default(); + let serialized = update_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), update_delegate.len()); + } + + #[test] + fn test_update_delegate_len() { + let update_delegates = vec![ + UpdateDelegate { + additional_delegates: vec![Pubkey::default()], + }, + UpdateDelegate { + additional_delegates: vec![Pubkey::default(), Pubkey::default()], + }, + ]; + for update_delegate in update_delegates { + let serialized = update_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), update_delegate.len()); + } + } +} diff --git a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs index cc51396f..d0774713 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/verified_creators.rs @@ -13,8 +13,10 @@ use crate::state::DataBlob; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] pub struct VerifiedCreatorsSignature { - address: Pubkey, // 32 - verified: bool, // 1 + /// The address of the creator. + pub address: Pubkey, // 32 + /// Whether or not the creator is verified. + pub verified: bool, // 1 } impl DataBlob for VerifiedCreatorsSignature { @@ -30,7 +32,7 @@ impl DataBlob for VerifiedCreatorsSignature { #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct VerifiedCreators { /// A list of signatures - signatures: Vec, // 4 + len * VerifiedCreatorsSignature + pub signatures: Vec, // 4 + len * VerifiedCreatorsSignature } impl DataBlob for VerifiedCreators { @@ -217,3 +219,43 @@ impl PluginValidation for VerifiedCreators { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_verified_creators_signature_len() { + let verified_creators_signature = VerifiedCreatorsSignature { + address: Pubkey::default(), + verified: false, + }; + let serialized = verified_creators_signature.try_to_vec().unwrap(); + assert_eq!(serialized.len(), verified_creators_signature.len()); + } + + #[test] + fn test_verified_creators_default_len() { + let verified_creators = VerifiedCreators { signatures: vec![] }; + let serialized = verified_creators.try_to_vec().unwrap(); + assert_eq!(serialized.len(), verified_creators.len()); + } + + #[test] + fn test_verified_creators_len() { + let verified_creators = VerifiedCreators { + signatures: vec![ + VerifiedCreatorsSignature { + address: Pubkey::default(), + verified: false, + }, + VerifiedCreatorsSignature { + address: Pubkey::default(), + verified: true, + }, + ], + }; + let serialized = verified_creators.try_to_vec().unwrap(); + assert_eq!(serialized.len(), verified_creators.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs index 4c5272b5..ebe86187 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs @@ -14,8 +14,10 @@ use crate::{ /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] pub struct AutographSignature { - address: Pubkey, // 32 - message: String, // 4 + len + /// The address of the creator. + pub address: Pubkey, // 32 + /// The message of the creator. + pub message: String, // 4 + len } impl DataBlob for AutographSignature { @@ -31,7 +33,7 @@ impl DataBlob for AutographSignature { #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] pub struct Autograph { /// A list of signatures with option message - signatures: Vec, // 4 + len * Autograph len + pub signatures: Vec, // 4 + len * Autograph len } fn validate_autograph( @@ -140,3 +142,43 @@ impl DataBlob for Autograph { Self::BASE_LEN + self.signatures.iter().map(|sig| sig.len()).sum::() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_autograph_signature_len() { + let autograph_signature = AutographSignature { + address: Pubkey::default(), + message: "test".to_string(), + }; + let serialized = autograph_signature.try_to_vec().unwrap(); + assert_eq!(serialized.len(), autograph_signature.len()); + } + + #[test] + fn test_autograph_default_len() { + let autograph = Autograph { signatures: vec![] }; + let serialized = autograph.try_to_vec().unwrap(); + assert_eq!(serialized.len(), autograph.len()); + } + + #[test] + fn test_autograph_len() { + let autograph = Autograph { + signatures: vec![ + AutographSignature { + address: Pubkey::default(), + message: "test".to_string(), + }, + AutographSignature { + address: Pubkey::default(), + message: "test2".to_string(), + }, + ], + }; + let serialized = autograph.try_to_vec().unwrap(); + assert_eq!(serialized.len(), autograph.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs index ff9843c8..f26c3fd5 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/burn_delegate.rs @@ -50,3 +50,15 @@ impl PluginValidation for BurnDelegate { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_burn_delegate_len() { + let burn_delegate = BurnDelegate::default(); + let serialized = burn_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), burn_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs index 87a842e2..1bd503af 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/freeze_delegate.rs @@ -107,3 +107,15 @@ impl PluginValidation for FreezeDelegate { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_freeze_delegate_len() { + let freeze_delegate = FreezeDelegate::default(); + let serialized = freeze_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), freeze_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs b/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs index 7da1ccc4..fc16e4de 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/transfer_delegate.rs @@ -51,3 +51,15 @@ impl PluginValidation for TransferDelegate { abstain!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_transfer_delegate_len() { + let transfer_delegate = TransferDelegate::default(); + let serialized = transfer_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), transfer_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs index 3515c66a..62664ead 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_burn_delegate.rs @@ -52,3 +52,15 @@ impl PluginValidation for PermanentBurnDelegate { abstain!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_permanent_burn_delegate_len() { + let permanent_burn_delegate = PermanentBurnDelegate::default(); + let serialized = permanent_burn_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), permanent_burn_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs index d6cba7dc..d7a6dea1 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_freeze_delegate.rs @@ -88,3 +88,15 @@ impl PluginValidation for PermanentFreezeDelegate { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_permanent_freeze_delegate_len() { + let permanent_freeze_delegate = PermanentFreezeDelegate::default(); + let serialized = permanent_freeze_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), permanent_freeze_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs b/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs index 147c5cd7..4f442efe 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/permanent_transfer_delegate.rs @@ -51,3 +51,15 @@ impl PluginValidation for PermanentTransferDelegate { abstain!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_permanent_transfer_delegate_len() { + let permanent_transfer_delegate = PermanentTransferDelegate::default(); + let serialized = permanent_transfer_delegate.try_to_vec().unwrap(); + assert_eq!(serialized.len(), permanent_transfer_delegate.len()); + } +} diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index c3f758d4..704a945f 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -5,14 +5,13 @@ use std::collections::BTreeMap; use crate::{ error::MplCoreError, + plugins::{ + ExternalPluginAdapter, ExternalPluginAdapterKey, ExternalRegistryRecord, Plugin, + PluginType, RegistryRecord, + }, state::{Authority, DataBlob, Key, UpdateAuthority}, }; -use super::{ - ExternalPluginAdapter, ExternalPluginAdapterKey, ExternalRegistryRecord, Plugin, PluginType, - RegistryRecord, -}; - /// Lifecycle permissions /// Plugins use this field to indicate their permission to approve or deny /// a lifecycle action. @@ -1113,3 +1112,20 @@ pub(crate) fn validate_external_plugin_adapter_checks<'a>( abstain!() } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_external_check_result_size() { + let fixture = ExternalCheckResult { flags: 0 }; + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } +} diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index 6b72d4ef..ec06ff52 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -19,7 +19,7 @@ use num_derive::ToPrimitive; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, }; -use strum::EnumCount; +use strum::{EnumCount, EnumIter}; use crate::{ error::MplCoreError, @@ -133,6 +133,7 @@ impl DataBlob for Plugin { EnumCount, PartialOrd, Ord, + EnumIter, )] pub enum PluginType { /// Royalties plugin. @@ -233,3 +234,191 @@ impl From for usize { plugin_type as usize } } + +#[cfg(test)] +mod test { + use solana_program::pubkey::Pubkey; + use strum::IntoEnumIterator; + + use super::*; + + #[test] + fn test_plugin_empty_size() { + //TODO: Implement Default for all plugins in a separate PR. + let plugins = vec![ + Plugin::Royalties(Royalties { + basis_points: 0, + creators: vec![], + rule_set: RuleSet::None, + }), + Plugin::FreezeDelegate(FreezeDelegate { frozen: false }), + Plugin::BurnDelegate(BurnDelegate {}), + Plugin::TransferDelegate(TransferDelegate {}), + Plugin::UpdateDelegate(UpdateDelegate { + additional_delegates: vec![], + }), + Plugin::PermanentFreezeDelegate(PermanentFreezeDelegate { frozen: false }), + Plugin::Attributes(Attributes { + attribute_list: vec![], + }), + Plugin::PermanentTransferDelegate(PermanentTransferDelegate {}), + Plugin::PermanentBurnDelegate(PermanentBurnDelegate {}), + Plugin::Edition(Edition { number: 0 }), + Plugin::MasterEdition(MasterEdition { + max_supply: None, + name: None, + uri: None, + }), + Plugin::AddBlocker(AddBlocker {}), + Plugin::ImmutableMetadata(ImmutableMetadata {}), + Plugin::VerifiedCreators(VerifiedCreators { signatures: vec![] }), + Plugin::Autograph(Autograph { signatures: vec![] }), + ]; + + assert_eq!( + plugins.len(), + PluginType::COUNT, + "All plugins should be tested" + ); + + for fixture in plugins { + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } + } + + #[test] + fn test_plugin_different_size() { + //TODO: Implement Default for all plugins in a separate PR. + let plugins: Vec> = vec![ + vec![ + Plugin::Royalties(Royalties { + basis_points: 0, + creators: vec![], + rule_set: RuleSet::None, + }), + Plugin::Royalties(Royalties { + basis_points: 1, + creators: vec![Creator { + address: Pubkey::default(), + percentage: 1, + }], + rule_set: RuleSet::ProgramAllowList(vec![]), + }), + Plugin::Royalties(Royalties { + basis_points: 2, + creators: vec![ + Creator { + address: Pubkey::default(), + percentage: 2, + }, + Creator { + address: Pubkey::default(), + percentage: 3, + }, + ], + rule_set: RuleSet::ProgramDenyList(vec![Pubkey::default()]), + }), + Plugin::Royalties(Royalties { + basis_points: 3, + creators: vec![ + Creator { + address: Pubkey::default(), + percentage: 3, + }, + Creator { + address: Pubkey::default(), + percentage: 4, + }, + Creator { + address: Pubkey::default(), + percentage: 5, + }, + ], + rule_set: RuleSet::ProgramDenyList(vec![Pubkey::default(), Pubkey::default()]), + }), + ], + vec![Plugin::FreezeDelegate(FreezeDelegate { frozen: true })], + vec![Plugin::BurnDelegate(BurnDelegate {})], + vec![Plugin::TransferDelegate(TransferDelegate {})], + vec![Plugin::UpdateDelegate(UpdateDelegate { + additional_delegates: vec![Pubkey::default(), Pubkey::default()], + })], + vec![Plugin::PermanentFreezeDelegate(PermanentFreezeDelegate { + frozen: true, + })], + vec![Plugin::Attributes(Attributes { + attribute_list: vec![ + Attribute { + key: "test".to_string(), + value: "test".to_string(), + }, + Attribute { + key: "test2".to_string(), + value: "test2".to_string(), + }, + ], + })], + vec![Plugin::PermanentTransferDelegate( + PermanentTransferDelegate {}, + )], + vec![Plugin::PermanentBurnDelegate(PermanentBurnDelegate {})], + vec![Plugin::Edition(Edition { number: 1 })], + vec![Plugin::MasterEdition(MasterEdition { + max_supply: Some(1), + name: Some("test".to_string()), + uri: Some("test".to_string()), + })], + vec![Plugin::AddBlocker(AddBlocker {})], + vec![Plugin::ImmutableMetadata(ImmutableMetadata {})], + vec![Plugin::VerifiedCreators(VerifiedCreators { + signatures: vec![VerifiedCreatorsSignature { + address: Pubkey::default(), + verified: true, + }], + })], + vec![Plugin::Autograph(Autograph { + signatures: vec![AutographSignature { + address: Pubkey::default(), + message: "test".to_string(), + }], + })], + ]; + + assert_eq!( + plugins.len(), + PluginType::COUNT, + "All plugins should be tested" + ); + + for fixtures in plugins { + for fixture in fixtures { + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } + } + } + + #[test] + fn test_plugin_type_size() { + for fixture in PluginType::iter() { + let serialized = fixture.try_to_vec().unwrap(); + assert_eq!( + serialized.len(), + fixture.len(), + "Serialized {:?} should match size returned by len()", + fixture + ); + } + } +} diff --git a/programs/mpl-core/src/plugins/plugin_header.rs b/programs/mpl-core/src/plugins/plugin_header.rs index ec5bb107..5bc480a5 100644 --- a/programs/mpl-core/src/plugins/plugin_header.rs +++ b/programs/mpl-core/src/plugins/plugin_header.rs @@ -28,3 +28,18 @@ impl SolanaAccount for PluginHeaderV1 { Key::PluginHeaderV1 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_plugin_header_v1_len() { + let header = PluginHeaderV1 { + key: Key::PluginHeaderV1, + plugin_registry_offset: 0, + }; + let serialized = header.try_to_vec().unwrap(); + assert_eq!(serialized.len(), header.len()); + } +} diff --git a/programs/mpl-core/src/plugins/plugin_registry.rs b/programs/mpl-core/src/plugins/plugin_registry.rs index b21b30e7..6f8e20d1 100644 --- a/programs/mpl-core/src/plugins/plugin_registry.rs +++ b/programs/mpl-core/src/plugins/plugin_registry.rs @@ -237,3 +237,148 @@ impl DataBlob for ExternalRegistryRecord { len } } + +#[cfg(test)] +mod tests { + use solana_program::pubkey::Pubkey; + + use super::*; + + #[test] + fn test_plugin_registry_v1_default_len() { + let registry = PluginRegistryV1 { + key: Key::PluginRegistryV1, + registry: vec![], + external_registry: vec![], + }; + let serialized = registry.try_to_vec().unwrap(); + assert_eq!(serialized.len(), registry.len()); + } + + #[test] + fn test_plugin_registry_v1_different_len() { + let registry = PluginRegistryV1 { + key: Key::PluginRegistryV1, + registry: vec![ + RegistryRecord { + plugin_type: PluginType::TransferDelegate, + authority: Authority::UpdateAuthority, + offset: 0, + }, + RegistryRecord { + plugin_type: PluginType::FreezeDelegate, + authority: Authority::Owner, + offset: 1, + }, + RegistryRecord { + plugin_type: PluginType::PermanentBurnDelegate, + authority: Authority::Address { + address: Pubkey::default(), + }, + offset: 2, + }, + ], + external_registry: vec![ + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::LifecycleHook, + authority: Authority::UpdateAuthority, + lifecycle_checks: None, + offset: 3, + data_offset: None, + data_len: None, + }, + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::Oracle, + authority: Authority::Owner, + lifecycle_checks: Some(vec![]), + offset: 3, + data_offset: Some(4), + data_len: None, + }, + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::AppData, + authority: Authority::Address { + address: Pubkey::default(), + }, + lifecycle_checks: Some(vec![( + HookableLifecycleEvent::Create, + ExternalCheckResult { flags: 5 }, + )]), + offset: 6, + data_offset: Some(7), + data_len: Some(8), + }, + ], + }; + let serialized = registry.try_to_vec().unwrap(); + assert_eq!(serialized.len(), registry.len()); + } + + #[test] + fn test_registry_record_len() { + let records = vec![ + RegistryRecord { + plugin_type: PluginType::TransferDelegate, + authority: Authority::UpdateAuthority, + offset: 0, + }, + RegistryRecord { + plugin_type: PluginType::FreezeDelegate, + authority: Authority::Owner, + offset: 1, + }, + RegistryRecord { + plugin_type: PluginType::PermanentBurnDelegate, + authority: Authority::Address { + address: Pubkey::default(), + }, + offset: 2, + }, + ]; + + for record in records { + let serialized = record.try_to_vec().unwrap(); + assert_eq!(serialized.len(), record.len()); + } + } + + #[test] + fn test_external_registry_record_len() { + let records = vec![ + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::LifecycleHook, + authority: Authority::UpdateAuthority, + lifecycle_checks: None, + offset: 3, + data_offset: None, + data_len: None, + }, + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::Oracle, + authority: Authority::Owner, + lifecycle_checks: Some(vec![]), + offset: 3, + data_offset: Some(4), + data_len: None, + }, + ExternalRegistryRecord { + plugin_type: ExternalPluginAdapterType::AppData, + authority: Authority::Address { + address: Pubkey::default(), + }, + lifecycle_checks: Some(vec![( + HookableLifecycleEvent::Create, + ExternalCheckResult { flags: 5 }, + )]), + offset: 6, + data_offset: Some(7), + data_len: Some(8), + }, + ]; + + for record in records { + let serialized = record.try_to_vec().unwrap(); + assert_eq!(serialized.len(), record.len()); + } + } +} diff --git a/programs/mpl-core/src/state/asset.rs b/programs/mpl-core/src/state/asset.rs index 5cbfccdc..00d8ca86 100644 --- a/programs/mpl-core/src/state/asset.rs +++ b/programs/mpl-core/src/state/asset.rs @@ -405,3 +405,42 @@ impl CoreAsset for AssetV1 { &self.owner } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_asset_len() { + let assets = vec![ + AssetV1 { + key: Key::AssetV1, + owner: Pubkey::default(), + update_authority: UpdateAuthority::None, + name: "".to_string(), + uri: "".to_string(), + seq: None, + }, + AssetV1 { + key: Key::AssetV1, + owner: Pubkey::default(), + update_authority: UpdateAuthority::Address(Pubkey::default()), + name: "test".to_string(), + uri: "test".to_string(), + seq: None, + }, + AssetV1 { + key: Key::AssetV1, + owner: Pubkey::default(), + update_authority: UpdateAuthority::Collection(Pubkey::default()), + name: "test2".to_string(), + uri: "test2".to_string(), + seq: Some(1), + }, + ]; + for asset in assets { + let serialized = asset.try_to_vec().unwrap(); + assert_eq!(serialized.len(), asset.len()); + } + } +} diff --git a/programs/mpl-core/src/state/collection.rs b/programs/mpl-core/src/state/collection.rs index 78da0bea..321ee6ae 100644 --- a/programs/mpl-core/src/state/collection.rs +++ b/programs/mpl-core/src/state/collection.rs @@ -376,3 +376,34 @@ impl CoreAsset for CollectionV1 { &self.update_authority } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_collection_len() { + let collections = vec![ + CollectionV1 { + key: Key::CollectionV1, + update_authority: Pubkey::default(), + name: "".to_string(), + uri: "".to_string(), + num_minted: 0, + current_size: 0, + }, + CollectionV1 { + key: Key::CollectionV1, + update_authority: Pubkey::default(), + name: "test".to_string(), + uri: "test".to_string(), + num_minted: 1, + current_size: 1, + }, + ]; + for collection in collections { + let serialized = collection.try_to_vec().unwrap(); + assert_eq!(serialized.len(), collection.len()); + } + } +} diff --git a/programs/mpl-core/src/state/hashed_asset.rs b/programs/mpl-core/src/state/hashed_asset.rs index 279d05e6..aecfe668 100644 --- a/programs/mpl-core/src/state/hashed_asset.rs +++ b/programs/mpl-core/src/state/hashed_asset.rs @@ -36,3 +36,15 @@ impl SolanaAccount for HashedAssetV1 { Key::HashedAssetV1 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hashed_asset_len() { + let hashed_asset = HashedAssetV1::new([0; 32]); + let serialized = hashed_asset.try_to_vec().unwrap(); + assert_eq!(serialized.len(), hashed_asset.len()); + } +} diff --git a/programs/mpl-core/src/state/mod.rs b/programs/mpl-core/src/state/mod.rs index 9d1e6872..82cad5d4 100644 --- a/programs/mpl-core/src/state/mod.rs +++ b/programs/mpl-core/src/state/mod.rs @@ -20,6 +20,7 @@ mod hashed_asset; pub use hashed_asset::*; mod traits; +use strum::{EnumCount, EnumIter}; pub use traits::*; mod update_authority; @@ -41,7 +42,9 @@ pub enum DataState { /// Variants representing the different types of authority that can have permissions over plugins. #[repr(u8)] -#[derive(Copy, Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive( + Copy, Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq, PartialOrd, Ord, EnumCount, +)] pub enum Authority { /// No authority, used for immutability. None, @@ -71,7 +74,16 @@ impl DataBlob for Authority { /// An enum representing account discriminators. #[derive( - Clone, Copy, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, ToPrimitive, FromPrimitive, + Clone, + Copy, + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + ToPrimitive, + FromPrimitive, + EnumIter, )] pub enum Key { /// Uninitialized or invalid account. @@ -96,9 +108,38 @@ impl DataBlob for Key { } } -// impl Key { -// /// Get the size of the Key. -// pub fn get_initial_size() -> usize { -// 1 -// } -// } +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use super::*; + + #[test] + fn test_authority_len() { + let authorities = vec![ + Authority::None, + Authority::Owner, + Authority::UpdateAuthority, + Authority::Address { + address: Pubkey::default(), + }, + ]; + assert_eq!( + authorities.len(), + Authority::COUNT, + "Must test all Authority variants" + ); + for authority in authorities { + let serialized = authority.try_to_vec().unwrap(); + assert_eq!(serialized.len(), authority.len()); + } + } + + #[test] + fn test_key_len() { + for key in Key::iter() { + let serialized = key.try_to_vec().unwrap(); + assert_eq!(serialized.len(), key.len()); + } + } +} From 1e9f3aa4b8da4939f558ae4814455c5b5d4975ef Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 9 Dec 2024 17:14:45 -0500 Subject: [PATCH 08/10] Fixing missing file. --- .../src/plugins/internal/permanent/edition.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/programs/mpl-core/src/plugins/internal/permanent/edition.rs b/programs/mpl-core/src/plugins/internal/permanent/edition.rs index b71cf533..7155bc3c 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/edition.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/edition.rs @@ -54,3 +54,15 @@ impl DataBlob for Edition { Self::BASE_LEN } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_edition_len() { + let edition = Edition { number: 1 }; + let serialized = edition.try_to_vec().unwrap(); + assert_eq!(serialized.len(), edition.len()); + } +} From e97959b7aa9119eafef6de2e4ffbb21ef5dd7f4d Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Tue, 10 Dec 2024 10:26:39 -0500 Subject: [PATCH 09/10] Fixing wrong call. --- programs/mpl-core/src/processor/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index 2e1e6f31..6095da39 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -187,7 +187,7 @@ fn update<'a>( // Get a set of all the plugins on the collection (if any). let plugin_set: HashSet<_> = - if new_collection_account.data_len() > new_collection.get_size() { + if new_collection_account.data_len() > new_collection.len() { let plugin_list = list_plugins::(new_collection_account)?; plugin_list.into_iter().collect() } else { From 15b90543ebfac9db238564feb714ccd7cffb8cba Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Tue, 10 Dec 2024 10:49:23 -0500 Subject: [PATCH 10/10] Removing unnecessary impl. --- programs/mpl-core/src/plugins/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index 115df659..d2648d19 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -237,12 +237,6 @@ pub(crate) struct PluginAuthorityPair { pub(crate) authority: Option, } -impl From for usize { - fn from(plugin_type: PluginType) -> Self { - plugin_type as usize - } -} - #[cfg(test)] mod test { use solana_program::pubkey::Pubkey;