diff --git a/clients/js/baseline.json b/clients/js/baseline.json new file mode 100644 index 00000000..b5d10b90 --- /dev/null +++ b/clients/js/baseline.json @@ -0,0 +1,92 @@ +[ + { + "name": "CU: create a new, empty asset", + "unit": "Compute Units", + "value": 9805 + }, + { + "name": "Space: create a new, empty asset", + "unit": "Bytes", + "value": 91 + }, + { + "name": "CU: create a new, empty asset with empty collection", + "unit": "Compute Units", + "value": 21421 + }, + { + "name": "Space: create a new, empty asset with empty collection", + "unit": "Bytes", + "value": 91 + }, + { + "name": "CU: create a new asset with plugins", + "unit": "Compute Units", + "value": 32680 + }, + { + "name": "Space: create a new asset with plugins", + "unit": "Bytes", + "value": 198 + }, + { + "name": "CU: create a new asset with plugins and empty collection", + "unit": "Compute Units", + "value": 38466 + }, + { + "name": "Space: create a new asset with plugins and empty collection", + "unit": "Bytes", + "value": 198 + }, + { + "name": "CU: list an asset", + "unit": "Compute Units", + "value": 27622 + }, + { + "name": "CU: sell an asset", + "unit": "Compute Units", + "value": 37792 + }, + { + "name": "CU: list an asset with empty collection", + "unit": "Compute Units", + "value": 35791 + }, + { + "name": "CU: sell an asset with empty collection", + "unit": "Compute Units", + "value": 51043 + }, + { + "name": "CU: list an asset with collection royalties", + "unit": "Compute Units", + "value": 37047 + }, + { + "name": "CU: sell an asset with collection royalties", + "unit": "Compute Units", + "value": 56560 + }, + { + "name": "CU: transfer an empty asset", + "unit": "Compute Units", + "value": 5261 + }, + { + "name": "CU: transfer an empty asset with empty collection", + "unit": "Compute Units", + "value": 8035 + }, + { + "name": "CU: transfer an asset with plugins", + "unit": "Compute Units", + "value": 11386 + }, + { + "name": "CU: transfer an asset with plugins and empty collection", + "unit": "Compute Units", + "value": 14160 + } +] \ No newline at end of file diff --git a/programs/mpl-core/src/plugins/external/app_data.rs b/programs/mpl-core/src/plugins/external/app_data.rs index bda1422b..4ae0f75d 100644 --- a/programs/mpl-core/src/plugins/external/app_data.rs +++ b/programs/mpl-core/src/plugins/external/app_data.rs @@ -1,7 +1,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; -use crate::plugins::abstain; +use crate::plugins::{abstain, AssetValidationCommon, AssetValidationContext}; use crate::plugins::{ Authority, ExternalPluginAdapterSchema, PluginValidation, PluginValidationContext, @@ -34,14 +34,18 @@ impl AppData { impl PluginValidation for AppData { fn validate_add_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } diff --git a/programs/mpl-core/src/plugins/external/lifecycle_hook.rs b/programs/mpl-core/src/plugins/external/lifecycle_hook.rs index 47ba162d..871e8344 100644 --- a/programs/mpl-core/src/plugins/external/lifecycle_hook.rs +++ b/programs/mpl-core/src/plugins/external/lifecycle_hook.rs @@ -2,8 +2,9 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::plugins::{ - abstain, Authority, ExternalCheckResult, ExternalPluginAdapterSchema, ExtraAccount, - HookableLifecycleEvent, PluginValidation, PluginValidationContext, ValidationResult, + abstain, AssetValidationCommon, AssetValidationContext, Authority, ExternalCheckResult, + ExternalPluginAdapterSchema, ExtraAccount, HookableLifecycleEvent, PluginValidation, + PluginValidationContext, ValidationResult, }; /// Lifecycle hook that CPIs into the `hooked_program`. This hook is used for any lifecycle events @@ -41,14 +42,18 @@ impl LifecycleHook { impl PluginValidation for LifecycleHook { fn validate_add_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } diff --git a/programs/mpl-core/src/plugins/external/linked_app_data.rs b/programs/mpl-core/src/plugins/external/linked_app_data.rs index 687db7b6..2a73e3f1 100644 --- a/programs/mpl-core/src/plugins/external/linked_app_data.rs +++ b/programs/mpl-core/src/plugins/external/linked_app_data.rs @@ -1,8 +1,12 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use crate::plugins::{ - Authority, ExternalPluginAdapterSchema, PluginValidation, PluginValidationContext, - ValidationResult, +use crate::{ + plugins::{ + AssetValidationCommon, AssetValidationContext, Authority, ExternalPluginAdapterSchema, + PluginValidation, PluginValidationContext, ValidationResult, + }, + state::Key, + utils::load_key, }; /// The app data third party plugin contains arbitrary data that can be written to by the @@ -31,10 +35,12 @@ impl LinkedAppData { impl PluginValidation for LinkedAppData { fn validate_create( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { solana_program::msg!("LinkedAppData::validate_create"); - if ctx.asset_info.is_some() { + if load_key(common.asset_info, 0)? == Key::AssetV1 { Ok(ValidationResult::Rejected) } else { Ok(ValidationResult::Pass) diff --git a/programs/mpl-core/src/plugins/external/linked_lifecycle_hook.rs b/programs/mpl-core/src/plugins/external/linked_lifecycle_hook.rs index a5da9409..7a82df32 100644 --- a/programs/mpl-core/src/plugins/external/linked_lifecycle_hook.rs +++ b/programs/mpl-core/src/plugins/external/linked_lifecycle_hook.rs @@ -2,8 +2,9 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::plugins::{ - Authority, ExternalCheckResult, ExternalPluginAdapterSchema, ExtraAccount, - HookableLifecycleEvent, PluginValidation, PluginValidationContext, ValidationResult, + AssetValidationCommon, AssetValidationContext, Authority, ExternalCheckResult, + ExternalPluginAdapterSchema, ExtraAccount, HookableLifecycleEvent, PluginValidation, + PluginValidationContext, ValidationResult, }; /// Lifecycle hook that CPIs into the `hooked_program`. This hook is used for any lifecycle events @@ -41,14 +42,18 @@ impl LinkedLifecycleHook { impl PluginValidation for LinkedLifecycleHook { fn validate_add_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { Ok(ValidationResult::Pass) } fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/external/oracle.rs b/programs/mpl-core/src/plugins/external/oracle.rs index bc0a20ef..9ff091ec 100644 --- a/programs/mpl-core/src/plugins/external/oracle.rs +++ b/programs/mpl-core/src/plugins/external/oracle.rs @@ -4,8 +4,9 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::error::MplCoreError; use crate::plugins::{ - abstain, Authority, ExternalCheckResult, ExternalValidationResult, ExtraAccount, - HookableLifecycleEvent, PluginValidation, PluginValidationContext, ValidationResult, + abstain, AssetValidationCommon, AssetValidationContext, Authority, ExternalCheckResult, + ExternalValidationResult, ExtraAccount, HookableLifecycleEvent, PluginValidation, + PluginValidationContext, ValidationResult, }; /// Oracle plugin that allows getting a `ValidationResult` for a lifecycle event from an arbitrary @@ -39,56 +40,92 @@ impl Oracle { impl PluginValidation for Oracle { fn validate_add_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } fn validate_create( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - self.validate_helper(ctx, HookableLifecycleEvent::Create) + self.validate_helper( + plugin_ctx, + common, + asset_ctx, + HookableLifecycleEvent::Create, + ) } fn validate_transfer( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - self.validate_helper(ctx, HookableLifecycleEvent::Transfer) + self.validate_helper( + plugin_ctx, + common, + asset_ctx, + HookableLifecycleEvent::Transfer, + ) } fn validate_burn( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - self.validate_helper(ctx, HookableLifecycleEvent::Burn) + self.validate_helper(plugin_ctx, common, asset_ctx, HookableLifecycleEvent::Burn) } fn validate_update( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - self.validate_helper(ctx, HookableLifecycleEvent::Update) + self.validate_helper( + plugin_ctx, + common, + asset_ctx, + HookableLifecycleEvent::Update, + ) } } impl Oracle { fn validate_helper( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, event: HookableLifecycleEvent, ) -> Result { let oracle_account = match &self.base_address_config { None => self.base_address, - Some(extra_account) => extra_account.derive(&self.base_address, ctx)?, + Some(extra_account) => { + extra_account.derive(&self.base_address, plugin_ctx, common, asset_ctx)? + } }; - let oracle_account = ctx - .accounts - .iter() - .find(|account| *account.key == oracle_account) - .ok_or(MplCoreError::MissingExternalPluginAdapterAccount)?; + let oracle_account = if let AssetValidationContext::Create { accounts } + | AssetValidationContext::Transfer { accounts, .. } + | AssetValidationContext::Burn { accounts, .. } + | AssetValidationContext::Update { accounts, .. } = asset_ctx + { + accounts + .iter() + .find(|account| *account.key == oracle_account) + .ok_or(MplCoreError::MissingExternalPluginAdapterAccount)? + } else { + return Err(MplCoreError::InvalidPlugin.into()); + }; let offset = self.results_offset.to_offset_usize(); diff --git a/programs/mpl-core/src/plugins/external_plugin_adapters.rs b/programs/mpl-core/src/plugins/external_plugin_adapters.rs index 7068807d..bfa3c778 100644 --- a/programs/mpl-core/src/plugins/external_plugin_adapters.rs +++ b/programs/mpl-core/src/plugins/external_plugin_adapters.rs @@ -12,11 +12,12 @@ use crate::{ }; use super::{ - AppData, AppDataInitInfo, AppDataUpdateInfo, Authority, DataSection, DataSectionInitInfo, - ExternalCheckResult, ExternalRegistryRecord, LifecycleHook, LifecycleHookInitInfo, - LifecycleHookUpdateInfo, LinkedAppData, LinkedAppDataInitInfo, LinkedAppDataUpdateInfo, - LinkedLifecycleHook, LinkedLifecycleHookInitInfo, LinkedLifecycleHookUpdateInfo, Oracle, - OracleInitInfo, OracleUpdateInfo, PluginValidation, PluginValidationContext, ValidationResult, + AppData, AppDataInitInfo, AppDataUpdateInfo, AssetValidationCommon, AssetValidationContext, + Authority, DataSection, DataSectionInitInfo, ExternalCheckResult, ExternalRegistryRecord, + LifecycleHook, LifecycleHookInitInfo, LifecycleHookUpdateInfo, LinkedAppData, + LinkedAppDataInitInfo, LinkedAppDataUpdateInfo, LinkedLifecycleHook, + LinkedLifecycleHookInitInfo, LinkedLifecycleHookUpdateInfo, Oracle, OracleInitInfo, + OracleUpdateInfo, PluginValidation, PluginValidationContext, ValidationResult, }; /// List of third party plugin types. @@ -183,19 +184,27 @@ impl ExternalPluginAdapter { /// Validate the add external plugin adapter lifecycle event. pub(crate) fn validate_create( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { solana_program::msg!("ExternalPluginAdapter::validate_create"); match external_plugin_adapter { ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_create(ctx) + lifecycle_hook.validate_create(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::Oracle(oracle) => { + oracle.validate_create(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::AppData(app_data) => { + app_data.validate_create(plugin_ctx, common, ctx) } - ExternalPluginAdapter::Oracle(oracle) => oracle.validate_create(ctx), - ExternalPluginAdapter::AppData(app_data) => app_data.validate_create(ctx), ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_create(ctx) + lifecycle_hook.validate_create(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::LinkedAppData(app_data) => { + app_data.validate_create(plugin_ctx, common, ctx) } - ExternalPluginAdapter::LinkedAppData(app_data) => app_data.validate_create(ctx), // Here we block the creation of a DataSection plugin because this is only done internally. ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Rejected), } @@ -204,18 +213,26 @@ impl ExternalPluginAdapter { /// Route the validation of the update action to the appropriate plugin. pub(crate) fn validate_update( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { match external_plugin_adapter { ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_update(ctx) + lifecycle_hook.validate_update(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::Oracle(oracle) => { + oracle.validate_update(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::AppData(app_data) => { + app_data.validate_update(plugin_ctx, common, ctx) } - ExternalPluginAdapter::Oracle(oracle) => oracle.validate_update(ctx), - ExternalPluginAdapter::AppData(app_data) => app_data.validate_update(ctx), ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_update(ctx) + lifecycle_hook.validate_update(plugin_ctx, common, ctx) + } + ExternalPluginAdapter::LinkedAppData(app_data) => { + app_data.validate_update(plugin_ctx, common, ctx) } - ExternalPluginAdapter::LinkedAppData(app_data) => app_data.validate_update(ctx), ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Pass), } } @@ -223,18 +240,26 @@ impl ExternalPluginAdapter { /// Route the validation of the burn action to the appropriate plugin. pub(crate) fn validate_burn( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match external_plugin_adapter { ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_burn(ctx) + lifecycle_hook.validate_burn(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::Oracle(oracle) => { + oracle.validate_burn(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::AppData(app_data) => { + app_data.validate_burn(plugin_ctx, common, asset_ctx) } - ExternalPluginAdapter::Oracle(oracle) => oracle.validate_burn(ctx), - ExternalPluginAdapter::AppData(app_data) => app_data.validate_burn(ctx), ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_burn(ctx) + lifecycle_hook.validate_burn(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::LinkedAppData(app_data) => { + app_data.validate_burn(plugin_ctx, common, asset_ctx) } - ExternalPluginAdapter::LinkedAppData(app_data) => app_data.validate_burn(ctx), ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Pass), } } @@ -242,18 +267,26 @@ impl ExternalPluginAdapter { /// Route the validation of the transfer action to the appropriate external plugin adapter. pub(crate) fn validate_transfer( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match external_plugin_adapter { ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_transfer(ctx) + lifecycle_hook.validate_transfer(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::Oracle(oracle) => { + oracle.validate_transfer(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::AppData(app_data) => { + app_data.validate_transfer(plugin_ctx, common, asset_ctx) } - ExternalPluginAdapter::Oracle(oracle) => oracle.validate_transfer(ctx), - ExternalPluginAdapter::AppData(app_data) => app_data.validate_transfer(ctx), ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_transfer(ctx) + lifecycle_hook.validate_transfer(plugin_ctx, common, asset_ctx) + } + ExternalPluginAdapter::LinkedAppData(app_data) => { + app_data.validate_transfer(plugin_ctx, common, asset_ctx) } - ExternalPluginAdapter::LinkedAppData(app_data) => app_data.validate_transfer(ctx), ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Pass), } } @@ -261,23 +294,25 @@ impl ExternalPluginAdapter { /// Validate the add external plugin adapter lifecycle event. pub(crate) fn validate_add_external_plugin_adapter( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match external_plugin_adapter { ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_add_external_plugin_adapter(ctx) + lifecycle_hook.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } ExternalPluginAdapter::Oracle(oracle) => { - oracle.validate_add_external_plugin_adapter(ctx) + oracle.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } ExternalPluginAdapter::AppData(app_data) => { - app_data.validate_add_external_plugin_adapter(ctx) + app_data.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_add_external_plugin_adapter(ctx) + lifecycle_hook.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } ExternalPluginAdapter::LinkedAppData(app_data) => { - app_data.validate_add_external_plugin_adapter(ctx) + app_data.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } // Here we block the creation of a DataSection plugin because this is only done internally. ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Rejected), @@ -287,12 +322,14 @@ impl ExternalPluginAdapter { /// Validate the add external plugin adapter lifecycle event. pub(crate) fn validate_update_external_plugin_adapter( external_plugin_adapter: &ExternalPluginAdapter, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let resolved_authorities = ctx + let resolved_authorities = plugin_ctx .resolved_authorities .ok_or(MplCoreError::InvalidAuthority)?; - let base_result = if resolved_authorities.contains(ctx.self_authority) { + let base_result = if resolved_authorities.contains(plugin_ctx.self_authority) { solana_program::msg!("Base: Approved"); ValidationResult::Approved } else { @@ -300,20 +337,18 @@ impl ExternalPluginAdapter { }; let result = match external_plugin_adapter { - ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_update_external_plugin_adapter(ctx) - } + ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => lifecycle_hook + .validate_update_external_plugin_adapter(plugin_ctx, common, asset_ctx), ExternalPluginAdapter::Oracle(oracle) => { - oracle.validate_update_external_plugin_adapter(ctx) + oracle.validate_update_external_plugin_adapter(plugin_ctx, common, asset_ctx) } ExternalPluginAdapter::AppData(app_data) => { - app_data.validate_update_external_plugin_adapter(ctx) - } - ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => { - lifecycle_hook.validate_update_external_plugin_adapter(ctx) + app_data.validate_update_external_plugin_adapter(plugin_ctx, common, asset_ctx) } + ExternalPluginAdapter::LinkedLifecycleHook(lifecycle_hook) => lifecycle_hook + .validate_update_external_plugin_adapter(plugin_ctx, common, asset_ctx), ExternalPluginAdapter::LinkedAppData(app_data) => { - app_data.validate_update_external_plugin_adapter(ctx) + app_data.validate_update_external_plugin_adapter(plugin_ctx, common, asset_ctx) } // Here we block the update of a DataSection plugin because this is only done internally. ExternalPluginAdapter::DataSection(_) => Ok(ValidationResult::Rejected), @@ -467,7 +502,9 @@ impl ExtraAccount { pub(crate) fn derive( &self, program_id: &Pubkey, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match self { ExtraAccount::PreconfiguredProgram { .. } => { @@ -476,7 +513,7 @@ impl ExtraAccount { Ok(pubkey) } ExtraAccount::PreconfiguredCollection { .. } => { - let collection = ctx + let collection = common .collection_info .ok_or(MplCoreError::MissingCollection)? .key; @@ -485,21 +522,22 @@ impl ExtraAccount { Ok(pubkey) } ExtraAccount::PreconfiguredOwner { .. } => { - let asset_info = ctx.asset_info.ok_or(MplCoreError::MissingAsset)?; - let owner = AssetV1::load(asset_info, 0)?.owner; + let owner = AssetV1::load(common.asset_info, 0)?.owner; let seeds = &[MPL_CORE_PREFIX.as_bytes(), owner.as_ref()]; let (pubkey, _bump) = Pubkey::find_program_address(seeds, program_id); Ok(pubkey) } ExtraAccount::PreconfiguredRecipient { .. } => { - let recipient = ctx.new_owner.ok_or(MplCoreError::MissingNewOwner)?.key; - let seeds = &[MPL_CORE_PREFIX.as_bytes(), recipient.as_ref()]; - let (pubkey, _bump) = Pubkey::find_program_address(seeds, program_id); - Ok(pubkey) + if let AssetValidationContext::Transfer { new_owner, .. } = asset_ctx { + let seeds = &[MPL_CORE_PREFIX.as_bytes(), new_owner.key.as_ref()]; + let (pubkey, _bump) = Pubkey::find_program_address(seeds, program_id); + Ok(pubkey) + } else { + Err(MplCoreError::MissingNewOwner.into()) + } } ExtraAccount::PreconfiguredAsset { .. } => { - let asset = ctx.asset_info.ok_or(MplCoreError::MissingAsset)?.key; - let seeds = &[MPL_CORE_PREFIX.as_bytes(), asset.as_ref()]; + let seeds = &[MPL_CORE_PREFIX.as_bytes(), common.asset_info.key.as_ref()]; let (pubkey, _bump) = Pubkey::find_program_address(seeds, program_id); Ok(pubkey) } @@ -508,7 +546,7 @@ impl ExtraAccount { custom_program_id, .. } => { - let seeds = transform_seeds(seeds, ctx)?; + let seeds = transform_seeds(seeds, plugin_ctx, common, asset_ctx)?; // Convert the Vec of Vec into Vec of u8 slices. let vec_of_slices: Vec<&[u8]> = seeds.iter().map(Vec::as_slice).collect(); @@ -527,14 +565,16 @@ impl ExtraAccount { // Transform seeds from their tokens into actual seeds based on passed-in context values. fn transform_seeds( seeds: &Vec, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result>, ProgramError> { let mut transformed_seeds = Vec::>::new(); for seed in seeds { match seed { Seed::Collection => { - let collection = ctx + let collection = common .collection_info .ok_or(MplCoreError::MissingCollection)? .key @@ -543,26 +583,19 @@ fn transform_seeds( transformed_seeds.push(collection); } Seed::Owner => { - let asset_info = ctx.asset_info.ok_or(MplCoreError::MissingAsset)?; - let owner = AssetV1::load(asset_info, 0)?.owner.as_ref().to_vec(); + let owner = AssetV1::load(common.asset_info, 0)?.owner.as_ref().to_vec(); transformed_seeds.push(owner); } Seed::Recipient => { - let recipient = ctx - .new_owner - .ok_or(MplCoreError::MissingNewOwner)? - .key - .as_ref() - .to_vec(); - transformed_seeds.push(recipient); + if let AssetValidationContext::Transfer { new_owner, .. } = asset_ctx { + let recipient = new_owner.key.as_ref().to_vec(); + transformed_seeds.push(recipient); + } else { + return Err(MplCoreError::MissingNewOwner.into()); + } } Seed::Asset => { - let asset = ctx - .asset_info - .ok_or(MplCoreError::MissingAsset)? - .key - .as_ref() - .to_vec(); + let asset = common.asset_info.key.as_ref().to_vec(); transformed_seeds.push(asset); } Seed::Address(pubkey) => { 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..6ab6bb6a 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 @@ -2,8 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ + error::MplCoreError, plugins::{ - abstain, reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult, + abstain, reject, AssetValidationCommon, AssetValidationContext, PluginType, + PluginValidation, PluginValidationContext, ValidationResult, }, state::{Authority, DataBlob}, }; @@ -28,16 +30,20 @@ impl DataBlob for AddBlocker { impl PluginValidation for AddBlocker { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(plugin) = ctx.target_plugin { - if plugin.manager() == Authority::Owner - || PluginType::from(plugin) == PluginType::AddBlocker + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + if new_plugin.manager() == Authority::Owner + || PluginType::from(new_plugin) == PluginType::AddBlocker { return abstain!(); } - } - reject!() + reject!() + } else { + Err(MplCoreError::InvalidPlugin.into()) + } } } 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..5fed9842 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 @@ -2,7 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::{reject, PluginValidation, PluginValidationContext, ValidationResult}, + plugins::{ + reject, AssetValidationCommon, AssetValidationContext, PluginValidation, + PluginValidationContext, ValidationResult, + }, state::DataBlob, }; @@ -27,7 +30,9 @@ impl PluginValidation for ImmutableMetadata { /// Validate the update lifecycle action. fn validate_update( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { reject!() } 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..73a9fcb5 100644 --- a/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs +++ b/programs/mpl-core/src/plugins/internal/authority_managed/royalties.rs @@ -6,7 +6,8 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::error::MplCoreError; use crate::plugins::{ - abstain, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, + abstain, reject, AssetValidationCommon, AssetValidationContext, Plugin, PluginValidation, + PluginValidationContext, ValidationResult, }; use crate::state::DataBlob; @@ -106,67 +107,85 @@ fn validate_royalties(royalties: &Royalties) -> Result Result { validate_royalties(self) } fn validate_transfer( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let new_owner = ctx.new_owner.ok_or(MplCoreError::MissingNewOwner)?; - match &self.rule_set { - RuleSet::None => abstain!(), - RuleSet::ProgramAllowList(allow_list) => { - if allow_list.contains(ctx.authority_info.owner) - && allow_list.contains(new_owner.owner) - { - abstain!() - } else { - reject!() + if let AssetValidationContext::Transfer { new_owner, .. } = asset_ctx { + match &self.rule_set { + RuleSet::None => abstain!(), + RuleSet::ProgramAllowList(allow_list) => { + if allow_list.contains(common.authority_info.owner) + && allow_list.contains(new_owner.owner) + { + abstain!() + } else { + reject!() + } } - } - RuleSet::ProgramDenyList(deny_list) => { - if deny_list.contains(ctx.authority_info.owner) - || deny_list.contains(new_owner.owner) - { - reject!() - } else { - abstain!() + RuleSet::ProgramDenyList(deny_list) => { + if deny_list.contains(common.authority_info.owner) + || deny_list.contains(new_owner.owner) + { + reject!() + } else { + abstain!() + } } } + } else { + Err(MplCoreError::MissingNewOwner.into()) } } fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - match ctx.target_plugin { - Some(Plugin::Royalties(_royalties)) => validate_royalties(self), - _ => abstain!(), + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + match new_plugin { + Plugin::Royalties(_royalties) => validate_royalties(self), + _ => abstain!(), + } + } else { + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_update_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let plugin_to_update = ctx.target_plugin.ok_or(MplCoreError::InvalidPlugin)?; - let resolved_authorities = ctx - .resolved_authorities - .ok_or(MplCoreError::InvalidAuthority)?; - - // Perform validation on the new royalties plugin data. - if let Plugin::Royalties(royalties) = plugin_to_update { - if resolved_authorities.contains(ctx.self_authority) { - validate_royalties(royalties) + if let AssetValidationContext::UpdatePlugin { new_plugin } = asset_ctx { + let resolved_authorities = plugin_ctx + .resolved_authorities + .ok_or(MplCoreError::InvalidAuthority)?; + + // Perform validation on the new royalties plugin data. + if let Plugin::Royalties(royalties) = new_plugin { + if resolved_authorities.contains(plugin_ctx.self_authority) { + validate_royalties(royalties) + } else { + abstain!() + } } else { abstain!() } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } } 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..9790c5d7 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 @@ -5,7 +5,7 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{ error::MplCoreError, - plugins::PluginType, + plugins::{AssetValidationCommon, AssetValidationContext, PluginType}, state::{Authority, DataBlob}, }; @@ -50,15 +50,20 @@ impl DataBlob for UpdateDelegate { impl PluginValidation for UpdateDelegate { fn validate_create( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(resolved_authorities) = ctx.resolved_authorities { - if resolved_authorities.contains(ctx.self_authority) { + if let Some(resolved_authorities) = plugin_ctx.resolved_authorities { + if resolved_authorities.contains(plugin_ctx.self_authority) { return approve!(); } } - if self.additional_delegates.contains(ctx.authority_info.key) { + if self + .additional_delegates + .contains(common.authority_info.key) + { return approve!(); } @@ -67,15 +72,19 @@ impl PluginValidation for UpdateDelegate { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(new_plugin) = ctx.target_plugin { - if ((ctx.resolved_authorities.is_some() - && ctx + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + if ((plugin_ctx.resolved_authorities.is_some() + && plugin_ctx .resolved_authorities .unwrap() - .contains(ctx.self_authority)) - || self.additional_delegates.contains(ctx.authority_info.key)) + .contains(plugin_ctx.self_authority)) + || self + .additional_delegates + .contains(common.authority_info.key)) && new_plugin.manager() == Authority::UpdateAuthority { approve!() @@ -89,15 +98,19 @@ impl PluginValidation for UpdateDelegate { fn validate_remove_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(plugin_to_remove) = ctx.target_plugin { - if ((ctx.resolved_authorities.is_some() - && ctx + if let AssetValidationContext::RemovePlugin { plugin_to_remove } = asset_ctx { + if ((plugin_ctx.resolved_authorities.is_some() + && plugin_ctx .resolved_authorities .unwrap() - .contains(ctx.self_authority)) - || self.additional_delegates.contains(ctx.authority_info.key)) + .contains(plugin_ctx.self_authority)) + || self + .additional_delegates + .contains(common.authority_info.key)) && plugin_to_remove.manager() == Authority::UpdateAuthority { approve!() @@ -112,102 +125,127 @@ impl PluginValidation for UpdateDelegate { // Validate the approve plugin authority lifecycle action. fn validate_approve_plugin_authority( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let plugin = ctx.target_plugin.ok_or(MplCoreError::InvalidPlugin)?; - - // If the plugin authority is the authority signing. - if ((ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority)) - // Or the authority is one of the additional delegates. - || self.additional_delegates.contains(ctx.authority_info.key)) - // And it's an authority-managed plugin. - && plugin.manager() == Authority::UpdateAuthority - // And the plugin is not an UpdateDelegate plugin, because we cannot change the authority of the UpdateDelegate plugin. - && PluginType::from(plugin) != PluginType::UpdateDelegate - { - solana_program::msg!("UpdateDelegate: Approved"); - Ok(ValidationResult::Approved) + if let AssetValidationContext::ApprovePluginAuthority { plugin } = asset_ctx { + // If the plugin authority is the authority signing. + if ((plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority)) + // Or the authority is one of the additional delegates. + || self.additional_delegates.contains(common.authority_info.key)) + // And it's an authority-managed plugin. + && plugin.manager() == Authority::UpdateAuthority + // And the plugin is not an UpdateDelegate plugin, because we cannot change the authority of the UpdateDelegate plugin. + && PluginType::from(plugin) != PluginType::UpdateDelegate + { + solana_program::msg!("UpdateDelegate: Approved"); + Ok(ValidationResult::Approved) + } else { + Ok(ValidationResult::Pass) + } } else { - Ok(ValidationResult::Pass) + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the revoke plugin authority lifecycle action. fn validate_revoke_plugin_authority( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let plugin = ctx.target_plugin.ok_or(MplCoreError::InvalidPlugin)?; - - // If the plugin authority is the authority signing. - if (ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority)) - // Or the authority is one of the additional delegates. - || (self.additional_delegates.contains(ctx.authority_info.key) && PluginType::from(plugin) != PluginType::UpdateDelegate) - // And it's an authority-managed plugin. - && plugin.manager() == Authority::UpdateAuthority - { - approve!() + if let AssetValidationContext::RevokePluginAuthority { plugin } = asset_ctx { + // If the plugin authority is the authority signing. + if (plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority)) + // Or the authority is one of the additional delegates. + || (self.additional_delegates.contains(common.authority_info.key) && PluginType::from(plugin) != PluginType::UpdateDelegate) + // And it's an authority-managed plugin. + && plugin.manager() == Authority::UpdateAuthority + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_update( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if ((ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority)) - || self.additional_delegates.contains(ctx.authority_info.key)) - // We do not allow the root authority (either Collection or Address) to be changed by this delegate. - && ctx.new_collection_authority.is_none() && ctx.new_asset_authority.is_none() + if let AssetValidationContext::Update { + new_update_authority, + .. + } = asset_ctx { - approve!() + if ((plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority)) + // Or the authority is one of the additional delegates. + || self.additional_delegates.contains(common.authority_info.key)) + // We do not allow the root authority (either Collection or Address) to be changed by this delegate. + && new_update_authority.is_none() + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_update_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let plugin = ctx.target_plugin.ok_or(MplCoreError::InvalidPlugin)?; - - // If the plugin itself is being updated. - if (ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority)) - || self.additional_delegates.contains(ctx.authority_info.key) - { - if let Plugin::UpdateDelegate(update_delegate) = plugin { - let existing: BTreeSet<_> = self.additional_delegates.iter().collect(); - let new: BTreeSet<_> = update_delegate.additional_delegates.iter().collect(); - - if existing.difference(&new).collect::>() == vec![&ctx.authority_info.key] - && new.difference(&existing).collect::>().is_empty() - { - solana_program::msg!("UpdateDelegate: Approved"); + if let AssetValidationContext::UpdatePlugin { new_plugin } = asset_ctx { + // If the plugin itself is being updated. + if (plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority)) + // Or the authority is one of the additional delegates. + || self.additional_delegates.contains(common.authority_info.key) + { + if let Plugin::UpdateDelegate(update_delegate) = new_plugin { + let existing: BTreeSet<_> = self.additional_delegates.iter().collect(); + let new: BTreeSet<_> = update_delegate.additional_delegates.iter().collect(); + + if existing.difference(&new).collect::>() + == vec![&common.authority_info.key] + && new.difference(&existing).collect::>().is_empty() + { + solana_program::msg!("UpdateDelegate: Approved"); + return Ok(ValidationResult::Approved); + } + } else { return Ok(ValidationResult::Approved); } - } else { - return Ok(ValidationResult::Approved); } - } - abstain!() + abstain!() + } else { + Err(MplCoreError::InvalidPlugin.into()) + } } } 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..0686e749 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 @@ -6,7 +6,8 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::error::MplCoreError; use crate::plugins::{ - abstain, Plugin, PluginValidation, PluginValidationContext, ValidationResult, + abstain, AssetValidationCommon, AssetValidationContext, Plugin, PluginValidation, + PluginValidationContext, ValidationResult, }; use crate::state::DataBlob; @@ -174,49 +175,67 @@ fn validate_verified_creators_as_plugin_authority( impl PluginValidation for VerifiedCreators { fn validate_create( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - validate_verified_creators_as_plugin_authority(self, None, ctx.authority_info.key) + validate_verified_creators_as_plugin_authority(self, None, common.authority_info.key) } fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - match ctx.target_plugin { - Some(Plugin::VerifiedCreators(_verified_creators)) => { - validate_verified_creators_as_plugin_authority(self, None, ctx.authority_info.key) + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + match new_plugin { + Plugin::VerifiedCreators(_verified_creators) => { + validate_verified_creators_as_plugin_authority( + self, + None, + common.authority_info.key, + ) + } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_update_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let resolved_authorities = ctx - .resolved_authorities - .ok_or(MplCoreError::InvalidAuthority)?; - match ctx.target_plugin { - Some(Plugin::VerifiedCreators(verified_creators)) => { - if resolved_authorities.contains(ctx.self_authority) { - validate_verified_creators_as_plugin_authority( - verified_creators, - Some(self), - ctx.authority_info.key, - )?; - Ok(ValidationResult::Approved) - } else { - validate_verified_creators_as_creator( - verified_creators, - self, - ctx.authority_info.key, - )?; - Ok(ValidationResult::Approved) + if let AssetValidationContext::UpdatePlugin { new_plugin } = asset_ctx { + let resolved_authorities = plugin_ctx + .resolved_authorities + .ok_or(MplCoreError::InvalidAuthority)?; + match new_plugin { + Plugin::VerifiedCreators(verified_creators) => { + if resolved_authorities.contains(plugin_ctx.self_authority) { + validate_verified_creators_as_plugin_authority( + verified_creators, + Some(self), + common.authority_info.key, + )?; + Ok(ValidationResult::Approved) + } else { + validate_verified_creators_as_creator( + verified_creators, + self, + common.authority_info.key, + )?; + Ok(ValidationResult::Approved) + } } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } } 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..20472f22 100644 --- a/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs +++ b/programs/mpl-core/src/plugins/internal/owner_managed/autograph.rs @@ -6,7 +6,8 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{ error::MplCoreError, plugins::{ - abstain, approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult, + abstain, approve, AssetValidationCommon, AssetValidationContext, Plugin, PluginValidation, + PluginValidationContext, ValidationResult, }, state::DataBlob, }; @@ -94,42 +95,57 @@ fn validate_autograph( impl PluginValidation for Autograph { fn validate_create( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - validate_autograph(self, None, ctx.authority_info.key, true) + validate_autograph(self, None, common.authority_info.key, true) } fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - match ctx.target_plugin { - Some(Plugin::Autograph(_autograph)) => { - validate_autograph(self, None, ctx.authority_info.key, true)?; - approve!() + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + match new_plugin { + Plugin::Autograph(_autograph) => { + validate_autograph(self, None, common.authority_info.key, true)?; + approve!() + } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_update_plugin( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let resolved_authorities = ctx + let resolved_authorities = plugin_ctx .resolved_authorities .ok_or(MplCoreError::InvalidAuthority)?; - match ctx.target_plugin { - Some(Plugin::Autograph(autograph)) => { - validate_autograph( - autograph, - Some(self), - ctx.authority_info.key, - resolved_authorities.contains(ctx.self_authority), - )?; - approve!() + + if let AssetValidationContext::UpdatePlugin { new_plugin } = asset_ctx { + match new_plugin { + Plugin::Autograph(autograph) => { + validate_autograph( + autograph, + Some(self), + common.authority_info.key, + resolved_authorities.contains(plugin_ctx.self_authority), + )?; + approve!() + } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } } 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..c61c6e25 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 @@ -2,7 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::{abstain, approve, PluginValidation, PluginValidationContext, ValidationResult}, + plugins::{ + abstain, approve, AssetValidationCommon, AssetValidationContext, PluginValidation, + PluginValidationContext, ValidationResult, + }, state::DataBlob, }; @@ -38,13 +41,15 @@ impl DataBlob for BurnDelegate { impl PluginValidation for BurnDelegate { fn validate_burn( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if ctx.resolved_authorities.is_some() - && ctx + if plugin_ctx.resolved_authorities.is_some() + && plugin_ctx .resolved_authorities .unwrap() - .contains(ctx.self_authority) + .contains(plugin_ctx.self_authority) { approve!() } else { 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..8ff39f83 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 @@ -2,9 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ + error::MplCoreError, plugins::{ - abstain, approve, reject, Plugin, PluginValidation, PluginValidationContext, - ValidationResult, + abstain, approve, reject, AssetValidationCommon, AssetValidationContext, Plugin, + PluginValidation, PluginValidationContext, ValidationResult, }, state::DataBlob, }; @@ -44,7 +45,9 @@ impl DataBlob for FreezeDelegate { impl PluginValidation for FreezeDelegate { fn validate_burn( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { if self.frozen { reject!() @@ -55,7 +58,9 @@ impl PluginValidation for FreezeDelegate { fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { if self.frozen { reject!() @@ -66,46 +71,64 @@ impl PluginValidation for FreezeDelegate { fn validate_approve_plugin_authority( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(Plugin::FreezeDelegate(freeze)) = ctx.target_plugin { - if freeze.frozen { - return reject!(); + if let AssetValidationContext::ApprovePluginAuthority { plugin } = asset_ctx { + if let Plugin::FreezeDelegate(freeze) = plugin { + if freeze.frozen { + return reject!(); + } } + abstain!() + } else { + Err(MplCoreError::InvalidPlugin.into()) } - abstain!() } /// Validate the revoke plugin authority lifecycle action. fn validate_revoke_plugin_authority( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(Plugin::FreezeDelegate(freeze)) = ctx.target_plugin { - if freeze.frozen { - return reject!(); - } else if ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority) - { - return approve!(); + if let AssetValidationContext::RevokePluginAuthority { plugin } = asset_ctx { + if let Plugin::FreezeDelegate(freeze) = plugin { + if freeze.frozen { + return reject!(); + } else if plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority) + { + return approve!(); + } } - } - abstain!() + abstain!() + } else { + Err(MplCoreError::InvalidPlugin.into()) + } } /// Validate the remove plugin lifecycle action. fn validate_remove_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if ctx.target_plugin.is_some() && self.frozen { - reject!() + if let AssetValidationContext::RemovePlugin { .. } = asset_ctx { + if self.frozen { + reject!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } } 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..d4d0c7d3 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 @@ -4,7 +4,8 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; use crate::plugins::{ - abstain, approve, PluginValidation, PluginValidationContext, ValidationResult, + abstain, approve, AssetValidationCommon, AssetValidationContext, PluginValidation, + PluginValidationContext, ValidationResult, }; /// This plugin manages the ability to transfer an asset and any authorities @@ -39,13 +40,15 @@ impl DataBlob for TransferDelegate { impl PluginValidation for TransferDelegate { fn validate_transfer( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if ctx.resolved_authorities.is_some() - && ctx + if plugin_ctx.resolved_authorities.is_some() + && plugin_ctx .resolved_authorities .unwrap() - .contains(ctx.self_authority) + .contains(plugin_ctx.self_authority) { return approve!(); } diff --git a/programs/mpl-core/src/plugins/internal/permanent/edition.rs b/programs/mpl-core/src/plugins/internal/permanent/edition.rs index 7ea3b475..ed5b79a6 100644 --- a/programs/mpl-core/src/plugins/internal/permanent/edition.rs +++ b/programs/mpl-core/src/plugins/internal/permanent/edition.rs @@ -2,8 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ + error::MplCoreError, plugins::{ - abstain, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, + abstain, reject, AssetValidationCommon, AssetValidationContext, Plugin, PluginValidation, + PluginValidationContext, ValidationResult, }, state::DataBlob, }; @@ -20,29 +22,41 @@ pub struct Edition { impl PluginValidation for Edition { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { // This plugin can only be added at creation time, so we // always reject it. - match ctx.target_plugin { - Some(Plugin::Edition(_edition)) => { - reject!() + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + match new_plugin { + Plugin::Edition(_edition) => { + reject!() + } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_remove_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { // This plugin cannot be removed // always reject it. - match ctx.target_plugin { - Some(Plugin::Edition(_edition)) => { - reject!() + if let AssetValidationContext::RemovePlugin { plugin_to_remove } = asset_ctx { + match plugin_to_remove { + Plugin::Edition(_edition) => { + reject!() + } + _ => abstain!(), } - _ => abstain!(), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } } 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..b914ecdd 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 @@ -2,9 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ + error::MplCoreError, plugins::{ - abstain, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, - ValidationResult, + abstain, force_approve, reject, AssetValidationCommon, AssetValidationContext, PluginType, + PluginValidation, PluginValidationContext, ValidationResult, }, state::DataBlob, }; @@ -28,25 +29,31 @@ impl DataBlob for PermanentBurnDelegate { impl PluginValidation for PermanentBurnDelegate { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { // This plugin can only be added at creation time, so we // always reject it. - if ctx.target_plugin.is_some() - && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentBurnDelegate - { - reject!() + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + if PluginType::from(new_plugin) == PluginType::PermanentBurnDelegate { + reject!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_burn( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(resolved_authorities) = ctx.resolved_authorities { - if resolved_authorities.contains(ctx.self_authority) { + if let Some(resolved_authorities) = plugin_ctx.resolved_authorities { + if resolved_authorities.contains(plugin_ctx.self_authority) { return force_approve!(); } } 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..85faac42 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 @@ -2,7 +2,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::{reject, PluginType}, + error::MplCoreError, + plugins::{reject, AssetValidationCommon, AssetValidationContext, PluginType}, state::DataBlob, }; @@ -43,7 +44,9 @@ impl DataBlob for PermanentFreezeDelegate { impl PluginValidation for PermanentFreezeDelegate { fn validate_burn( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { if self.frozen { reject!() @@ -54,7 +57,9 @@ impl PluginValidation for PermanentFreezeDelegate { fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { if self.frozen { reject!() @@ -65,25 +70,31 @@ impl PluginValidation for PermanentFreezeDelegate { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { // This plugin can only be added at creation time, so we // always reject it. - if ctx.target_plugin.is_some() - && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentFreezeDelegate - { - reject!() + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + if PluginType::from(new_plugin) == PluginType::PermanentFreezeDelegate { + reject!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the remove plugin lifecycle action. fn validate_remove_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if ctx.target_plugin.is_some() && self.frozen { + if self.frozen { reject!() } else { abstain!() 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..fcbaae75 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 @@ -1,11 +1,12 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; +use crate::error::MplCoreError; use crate::state::DataBlob; use crate::plugins::{ - abstain, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, - ValidationResult, + abstain, force_approve, reject, AssetValidationCommon, AssetValidationContext, PluginType, + PluginValidation, PluginValidationContext, ValidationResult, }; /// The permanent transfer plugin allows any authority to transfer the asset. @@ -27,25 +28,31 @@ impl DataBlob for PermanentTransferDelegate { impl PluginValidation for PermanentTransferDelegate { fn validate_add_plugin( &self, - ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { // This plugin can only be added at creation time, so we // always reject it. - if ctx.target_plugin.is_some() - && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentTransferDelegate - { - reject!() + if let AssetValidationContext::AddPlugin { new_plugin } = asset_ctx { + if PluginType::from(new_plugin) == PluginType::PermanentTransferDelegate { + reject!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } fn validate_transfer( &self, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { - if let Some(resolved_authorities) = ctx.resolved_authorities { - if resolved_authorities.contains(ctx.self_authority) { + if let Some(resolved_authorities) = plugin_ctx.resolved_authorities { + if resolved_authorities.contains(plugin_ctx.self_authority) { return force_approve!(); } } diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 2f47c5ed..d8b711a6 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -1,18 +1,140 @@ use borsh::{BorshDeserialize, BorshSerialize}; use modular_bitfield::{bitfield, specifiers::B29}; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +use solana_program::{account_info::AccountInfo, program_error::ProgramError}; use std::collections::BTreeMap; +use strum::EnumCount; use crate::{ error::MplCoreError, - state::{Authority, Key, UpdateAuthority}, + state::{AssetV1, Authority, CollectionV1, Key, UpdateAuthority}, + utils::load_key, }; use super::{ - ExternalPluginAdapter, ExternalPluginAdapterKey, ExternalRegistryRecord, Plugin, PluginType, - RegistryRecord, + ExternalPluginAdapter, ExternalPluginAdapterInitInfo, ExternalPluginAdapterKey, + ExternalPluginAdapterUpdateInfo, ExternalRegistryRecord, Plugin, PluginType, RegistryRecord, }; +/// Lifecycle Events +#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumCount)] +pub(crate) enum LifecycleEvent { + AddExternalPluginAdapter, + AddPlugin, + ApprovePluginAuthority, + Burn, + Compress, + Create, + Decompress, + RemoveExternalPluginAdapter, + RemovePlugin, + RevokePluginAuthority, + Transfer, + // UpdateExternalPluginAdapter, + UpdatePlugin, + Update, +} + +impl LifecycleEvent { + const ASSET_CHECKS: [fn() -> CheckResult; Self::COUNT] = [ + AssetV1::check_add_external_plugin_adapter, + AssetV1::check_add_plugin, + AssetV1::check_approve_plugin_authority, + AssetV1::check_burn, + AssetV1::check_compress, + AssetV1::check_create, + AssetV1::check_decompress, + AssetV1::check_remove_external_plugin_adapter, + AssetV1::check_remove_plugin, + AssetV1::check_revoke_plugin_authority, + AssetV1::check_transfer, + AssetV1::check_update_plugin, + AssetV1::check_update, + ]; + + const COLLECTION_CHECKS: [fn() -> CheckResult; Self::COUNT] = [ + CollectionV1::check_add_external_plugin_adapter, + CollectionV1::check_add_plugin, + CollectionV1::check_approve_plugin_authority, + CollectionV1::check_burn, + CollectionV1::check_compress, + CollectionV1::check_create, + CollectionV1::check_decompress, + CollectionV1::check_remove_external_plugin_adapter, + CollectionV1::check_remove_plugin, + CollectionV1::check_revoke_plugin_authority, + CollectionV1::check_transfer, + CollectionV1::check_update_plugin, + CollectionV1::check_update, + ]; + + const ASSET_VALIDATORS: [fn( + &AssetV1, + // &AccountInfo, + // Option<&Plugin>, + // Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, + ) -> Result; Self::COUNT] = [ + AssetV1::validate_add_external_plugin_adapter, + AssetV1::validate_add_plugin, + AssetV1::validate_approve_plugin_authority, + AssetV1::validate_burn, + AssetV1::validate_compress, + AssetV1::validate_create, + AssetV1::validate_decompress, + AssetV1::validate_remove_external_plugin_adapter, + AssetV1::validate_remove_plugin, + AssetV1::validate_revoke_plugin_authority, + AssetV1::validate_transfer, + AssetV1::validate_update_plugin, + AssetV1::validate_update, + ]; + + const COLLECTION_VALIDATORS: [fn( + &CollectionV1, + // &AccountInfo, + // Option<&Plugin>, + // Option<&ExternalPluginAdapter>, + &AssetValidationCommon, + &AssetValidationContext, + ) -> Result; Self::COUNT] = [ + CollectionV1::validate_add_external_plugin_adapter, + CollectionV1::validate_add_plugin, + CollectionV1::validate_approve_plugin_authority, + CollectionV1::validate_burn, + CollectionV1::validate_compress, + CollectionV1::validate_create, + CollectionV1::validate_decompress, + CollectionV1::validate_remove_external_plugin_adapter, + CollectionV1::validate_remove_plugin, + CollectionV1::validate_revoke_plugin_authority, + CollectionV1::validate_transfer, + CollectionV1::validate_update_plugin, + CollectionV1::validate_update, + ]; + + const PLUGIN_VALIDATORS: [fn( + &Plugin, + &PluginValidationContext, + &AssetValidationCommon, + &AssetValidationContext, + ) -> Result; Self::COUNT] = [ + Plugin::validate_add_external_plugin_adapter, + Plugin::validate_add_plugin, + Plugin::validate_approve_plugin_authority, + Plugin::validate_burn, + Plugin::validate_compress, + Plugin::validate_create, + Plugin::validate_decompress, + Plugin::validate_remove_external_plugin_adapter, + Plugin::validate_remove_plugin, + Plugin::validate_revoke_plugin_authority, + Plugin::validate_transfer, + Plugin::validate_update_plugin, + Plugin::validate_update, + ]; +} + /// Lifecycle permissions /// Plugins use this field to indicate their permission to approve or deny /// a lifecycle action. @@ -217,249 +339,368 @@ impl Plugin { /// Validate the add plugin lifecycle event. pub(crate) fn validate_add_plugin( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_add_plugin(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_add_plugin(ctx), - Plugin::BurnDelegate(burn) => burn.validate_add_plugin(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_add_plugin(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_add_plugin(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => burn.validate_add_plugin(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_add_plugin(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_add_plugin(ctx) + permanent_freeze.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_add_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_add_plugin(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_add_plugin(ctx) + permanent_transfer.validate_add_plugin(plugin_ctx, common, asset_ctx) } Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_add_plugin(ctx) + permanent_burn.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_add_plugin(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_add_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Edition(edition) => edition.validate_add_plugin(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_add_plugin(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_add_plugin(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_add_plugin(ctx) + immutable_metadata.validate_add_plugin(plugin_ctx, common, asset_ctx) } Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_add_plugin(ctx) + verified_creators.validate_add_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_add_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Autograph(autograph) => autograph.validate_add_plugin(ctx), } } /// Validate the remove plugin lifecycle event. pub(crate) fn validate_remove_plugin( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - if ctx.self_authority == &Authority::None - && ctx.target_plugin.is_some() - && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::from(plugin) - { - return reject!(); - } - - match plugin { - Plugin::Royalties(royalties) => royalties.validate_remove_plugin(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_remove_plugin(ctx), - Plugin::BurnDelegate(burn) => burn.validate_remove_plugin(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_remove_plugin(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_remove_plugin(ctx), - Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_remove_plugin(ctx) + if let AssetValidationContext::RemovePlugin { plugin_to_remove } = asset_ctx { + if plugin_ctx.self_authority == &Authority::None + && PluginType::from(plugin_to_remove) == PluginType::from(plugin) + { + return reject!(); } - Plugin::Attributes(attributes) => attributes.validate_remove_plugin(ctx), - Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_remove_plugin(ctx) - } - Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_remove_plugin(ctx) - } - Plugin::Edition(edition) => edition.validate_remove_plugin(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_remove_plugin(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_remove_plugin(ctx), - Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_remove_plugin(ctx) - } - Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_remove_plugin(ctx) + + match plugin { + Plugin::Royalties(royalties) => { + royalties.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => { + burn.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::TransferDelegate(transfer) => { + transfer.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentFreezeDelegate(permanent_freeze) => { + permanent_freeze.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentTransferDelegate(permanent_transfer) => { + permanent_transfer.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(permanent_burn) => { + permanent_burn.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => { + edition.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::MasterEdition(master_edition) => { + master_edition.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::ImmutableMetadata(immutable_metadata) => { + immutable_metadata.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_remove_plugin(plugin_ctx, common, asset_ctx) + } } - Plugin::Autograph(autograph) => autograph.validate_remove_plugin(ctx), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the approve plugin authority lifecycle event. pub(crate) fn validate_approve_plugin_authority( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - // Universally, we cannot delegate a plugin authority if it's already delegated, even if - // we're the manager. - if let Some(plugin_to_approve) = ctx.target_plugin { - if plugin_to_approve == plugin && &plugin_to_approve.manager() != ctx.self_authority { + if let AssetValidationContext::ApprovePluginAuthority { + plugin: plugin_to_approve, + } = asset_ctx + { + // Universally, we cannot delegate a plugin authority if it's already delegated, even if + // we're the manager. + if plugin_to_approve == plugin + && &plugin_to_approve.manager() != plugin_ctx.self_authority + { return Err(MplCoreError::CannotRedelegate.into()); } - } else { - return Err(MplCoreError::InvalidPlugin.into()); - } - match plugin { - Plugin::Royalties(royalties) => royalties.validate_approve_plugin_authority(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_approve_plugin_authority(ctx), - Plugin::BurnDelegate(burn) => burn.validate_approve_plugin_authority(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_approve_plugin_authority(ctx), - Plugin::UpdateDelegate(update_delegate) => { - update_delegate.validate_approve_plugin_authority(ctx) - } - Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_approve_plugin_authority(ctx) - } - Plugin::Attributes(attributes) => attributes.validate_approve_plugin_authority(ctx), - Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_approve_plugin_authority(ctx) - } - Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_approve_plugin_authority(ctx) - } - Plugin::Edition(edition) => edition.validate_approve_plugin_authority(ctx), - Plugin::MasterEdition(master_edition) => { - master_edition.validate_approve_plugin_authority(ctx) - } - Plugin::AddBlocker(add_blocker) => add_blocker.validate_approve_plugin_authority(ctx), - Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_approve_plugin_authority(ctx) - } - Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_approve_plugin_authority(ctx) + match plugin { + Plugin::Royalties(royalties) => { + royalties.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => { + burn.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::TransferDelegate(transfer) => { + transfer.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentFreezeDelegate(permanent_freeze) => permanent_freeze + .validate_approve_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::Attributes(attributes) => { + attributes.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentTransferDelegate(permanent_transfer) => permanent_transfer + .validate_approve_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::PermanentBurnDelegate(permanent_burn) => { + permanent_burn.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => { + edition.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::MasterEdition(master_edition) => { + master_edition.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata + .validate_approve_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::VerifiedCreators(verified_creators) => verified_creators + .validate_approve_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::Autograph(autograph) => { + autograph.validate_approve_plugin_authority(plugin_ctx, common, asset_ctx) + } } - Plugin::Autograph(autograph) => autograph.validate_approve_plugin_authority(ctx), + } else { + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the revoke plugin authority lifecycle event. pub(crate) fn validate_revoke_plugin_authority( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let target_plugin = ctx.target_plugin.ok_or(MplCoreError::InvalidPlugin)?; - - // If the plugin being checked is Authority::None then it can't be revoked. - if ctx.self_authority == &Authority::None - && PluginType::from(target_plugin) == PluginType::from(plugin) + if let AssetValidationContext::RevokePluginAuthority { + plugin: plugin_to_revoke, + } = asset_ctx { - return reject!(); - } + // If the plugin being checked is Authority::None then it can't be revoked. + if plugin_ctx.self_authority == &Authority::None + && PluginType::from(plugin_to_revoke) == PluginType::from(plugin) + { + return reject!(); + } + + let base_result = if PluginType::from(plugin_to_revoke) == PluginType::from(plugin) + && plugin_ctx.resolved_authorities.is_some() + && plugin_ctx + .resolved_authorities + .unwrap() + .contains(plugin_ctx.self_authority) + { + solana_program::msg!("Base: Approved"); + ValidationResult::Approved + } else { + ValidationResult::Pass + }; - let base_result = if PluginType::from(target_plugin) == PluginType::from(plugin) - && ctx.resolved_authorities.is_some() - && ctx - .resolved_authorities - .unwrap() - .contains(ctx.self_authority) - { - solana_program::msg!("Base: Approved"); - ValidationResult::Approved - } else { - ValidationResult::Pass - }; + let result = + match plugin { + Plugin::Royalties(royalties) => { + royalties.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => { + burn.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::TransferDelegate(transfer) => { + transfer.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => update_delegate + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::PermanentFreezeDelegate(permanent_freeze) => permanent_freeze + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::Attributes(attributes) => { + attributes.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentTransferDelegate(permanent_transfer) => permanent_transfer + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::PermanentBurnDelegate(permanent_burn) => permanent_burn + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::Edition(edition) => { + edition.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::MasterEdition(master_edition) => master_edition + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::VerifiedCreators(verified_creators) => verified_creators + .validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx), + Plugin::Autograph(autograph) => { + autograph.validate_revoke_plugin_authority(plugin_ctx, common, asset_ctx) + } + }?; - let result = match plugin { - Plugin::Royalties(royalties) => royalties.validate_revoke_plugin_authority(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_revoke_plugin_authority(ctx), - Plugin::BurnDelegate(burn) => burn.validate_revoke_plugin_authority(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_revoke_plugin_authority(ctx), - Plugin::UpdateDelegate(update_delegate) => { - update_delegate.validate_revoke_plugin_authority(ctx) - } - Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_revoke_plugin_authority(ctx) - } - Plugin::Attributes(attributes) => attributes.validate_revoke_plugin_authority(ctx), - Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_revoke_plugin_authority(ctx) - } - Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_revoke_plugin_authority(ctx) - } - Plugin::Edition(edition) => edition.validate_revoke_plugin_authority(ctx), - Plugin::MasterEdition(master_edition) => { - master_edition.validate_revoke_plugin_authority(ctx) - } - Plugin::AddBlocker(add_blocker) => add_blocker.validate_revoke_plugin_authority(ctx), - Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_revoke_plugin_authority(ctx) - } - Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_revoke_plugin_authority(ctx) + if result == ValidationResult::Pass { + Ok(base_result) + } else { + Ok(result) } - Plugin::Autograph(autograph) => autograph.validate_revoke_plugin_authority(ctx), - }?; - - if result == ValidationResult::Pass { - Ok(base_result) } else { - Ok(result) + Err(MplCoreError::InvalidPlugin.into()) } } /// Route the validation of the create action to the appropriate plugin. pub(crate) fn validate_create( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_create(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_create(ctx), - Plugin::BurnDelegate(burn) => burn.validate_create(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_create(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_create(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => freeze.validate_create(plugin_ctx, common, asset_ctx), + Plugin::BurnDelegate(burn) => burn.validate_create(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_create(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_create(ctx) + permanent_freeze.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_create(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_create(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_create(ctx) + permanent_transfer.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(permanent_burn) => { + permanent_burn.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_create(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_create(plugin_ctx, common, asset_ctx) } - Plugin::PermanentBurnDelegate(permanent_burn) => permanent_burn.validate_create(ctx), - Plugin::Edition(edition) => edition.validate_create(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_create(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_create(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_create(ctx) + immutable_metadata.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_create(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_create(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_create(ctx), - Plugin::Autograph(autograph) => autograph.validate_create(ctx), } } /// Route the validation of the update action to the appropriate plugin. pub(crate) fn validate_update( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_update(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_update(ctx), - Plugin::BurnDelegate(burn) => burn.validate_update(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_update(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_update(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => freeze.validate_update(plugin_ctx, common, asset_ctx), + Plugin::BurnDelegate(burn) => burn.validate_update(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_update(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_update(ctx) + permanent_freeze.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_update(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_update(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_update(ctx) + permanent_transfer.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(permanent_burn) => { + permanent_burn.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_update(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_update(plugin_ctx, common, asset_ctx) } - Plugin::PermanentBurnDelegate(permanent_burn) => permanent_burn.validate_update(ctx), - Plugin::Edition(edition) => edition.validate_update(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_update(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_update(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_update(ctx) + immutable_metadata.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_update(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_update(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_update(ctx), - Plugin::Autograph(autograph) => autograph.validate_update(ctx), } } @@ -467,12 +708,14 @@ impl Plugin { /// There is no check for updating a plugin because the plugin itself MUST validate the change. pub(crate) fn validate_update_plugin( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { - let resolved_authorities = ctx + let resolved_authorities = plugin_ctx .resolved_authorities .ok_or(MplCoreError::InvalidAuthority)?; - let base_result = if resolved_authorities.contains(ctx.self_authority) { + let base_result = if resolved_authorities.contains(plugin_ctx.self_authority) { solana_program::msg!("Base: Approved"); ValidationResult::Approved } else { @@ -480,31 +723,51 @@ impl Plugin { }; let result = match plugin { - Plugin::Royalties(royalties) => royalties.validate_update_plugin(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_update_plugin(ctx), - Plugin::BurnDelegate(burn) => burn.validate_update_plugin(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_update_plugin(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_update_plugin(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => { + burn.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::TransferDelegate(transfer) => { + transfer.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_update_plugin(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_update_plugin(ctx) + permanent_freeze.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_update_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_update_plugin(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_update_plugin(ctx) + permanent_transfer.validate_update_plugin(plugin_ctx, common, asset_ctx) } Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_update_plugin(ctx) + permanent_burn.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => { + edition.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::MasterEdition(master_edition) => { + master_edition.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_update_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Edition(edition) => edition.validate_update_plugin(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_update_plugin(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_update_plugin(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_update_plugin(ctx) + immutable_metadata.validate_update_plugin(plugin_ctx, common, asset_ctx) } Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_update_plugin(ctx) + verified_creators.validate_update_plugin(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_update_plugin(plugin_ctx, common, asset_ctx) } - Plugin::Autograph(autograph) => autograph.validate_update_plugin(ctx), }?; match (&base_result, &result) { @@ -530,208 +793,304 @@ impl Plugin { /// Route the validation of the burn action to the appropriate plugin. pub(crate) fn validate_burn( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_burn(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_burn(ctx), - Plugin::BurnDelegate(burn) => burn.validate_burn(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_burn(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_burn(ctx), + Plugin::Royalties(royalties) => royalties.validate_burn(plugin_ctx, common, asset_ctx), + Plugin::FreezeDelegate(freeze) => freeze.validate_burn(plugin_ctx, common, asset_ctx), + Plugin::BurnDelegate(burn) => burn.validate_burn(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_burn(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_burn(ctx) + permanent_freeze.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_burn(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_burn(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_burn(ctx) - } - Plugin::PermanentBurnDelegate(permanent_burn) => permanent_burn.validate_burn(ctx), - Plugin::Edition(edition) => edition.validate_burn(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_burn(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_burn(ctx), - Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata.validate_burn(ctx), - Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_burn(ctx), - Plugin::Autograph(autograph) => autograph.validate_burn(ctx), + permanent_transfer.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(permanent_burn) => { + permanent_burn.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_burn(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::ImmutableMetadata(immutable_metadata) => { + immutable_metadata.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_burn(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => autograph.validate_burn(plugin_ctx, common, asset_ctx), } } /// Route the validation of the transfer action to the appropriate plugin. pub(crate) fn validate_transfer( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_transfer(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_transfer(ctx), - Plugin::BurnDelegate(burn) => burn.validate_transfer(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_transfer(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_transfer(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => burn.validate_transfer(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_transfer(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_transfer(ctx) + permanent_freeze.validate_transfer(plugin_ctx, common, asset_ctx) } Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_transfer(ctx) + permanent_transfer.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes_transfer) => { + attributes_transfer.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(burn_transfer) => { + burn_transfer.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_transfer(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_transfer(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes_transfer) => attributes_transfer.validate_transfer(ctx), - Plugin::PermanentBurnDelegate(burn_transfer) => burn_transfer.validate_transfer(ctx), - Plugin::Edition(edition) => edition.validate_transfer(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_transfer(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_transfer(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_transfer(ctx) + immutable_metadata.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_transfer(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_transfer(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_transfer(ctx), - Plugin::Autograph(autograph) => autograph.validate_transfer(ctx), } } /// Route the validation of the compress action to the appropriate plugin. pub(crate) fn validate_compress( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_compress(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_compress(ctx), - Plugin::BurnDelegate(burn) => burn.validate_compress(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_compress(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_compress(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => burn.validate_compress(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_compress(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_compress(ctx) + permanent_freeze.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_compress(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_compress(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_compress(ctx) + permanent_transfer.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::PermanentBurnDelegate(burn_transfer) => { + burn_transfer.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_compress(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_compress(plugin_ctx, common, asset_ctx) } - Plugin::PermanentBurnDelegate(burn_transfer) => burn_transfer.validate_compress(ctx), - Plugin::Edition(edition) => edition.validate_compress(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_compress(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_compress(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_compress(ctx) + immutable_metadata.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_compress(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_compress(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_compress(ctx), - Plugin::Autograph(autograph) => autograph.validate_compress(ctx), } } /// Route the validation of the decompress action to the appropriate plugin. pub(crate) fn validate_decompress( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_decompress(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_decompress(ctx), - Plugin::BurnDelegate(burn) => burn.validate_decompress(ctx), - Plugin::TransferDelegate(transfer) => transfer.validate_decompress(ctx), - Plugin::UpdateDelegate(update_delegate) => update_delegate.validate_decompress(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => burn.validate_decompress(plugin_ctx, common, asset_ctx), + Plugin::TransferDelegate(transfer) => { + transfer.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::UpdateDelegate(update_delegate) => { + update_delegate.validate_decompress(plugin_ctx, common, asset_ctx) + } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_decompress(ctx) + permanent_freeze.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::Attributes(attributes) => { + attributes.validate_decompress(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_decompress(ctx), Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_decompress(ctx) + permanent_transfer.validate_decompress(plugin_ctx, common, asset_ctx) } Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_decompress(ctx) + permanent_burn.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => edition.validate_decompress(plugin_ctx, common, asset_ctx), + Plugin::MasterEdition(master_edition) => { + master_edition.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::AddBlocker(add_blocker) => { + add_blocker.validate_decompress(plugin_ctx, common, asset_ctx) } - Plugin::Edition(edition) => edition.validate_decompress(ctx), - Plugin::MasterEdition(master_edition) => master_edition.validate_decompress(ctx), - Plugin::AddBlocker(add_blocker) => add_blocker.validate_decompress(ctx), Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_decompress(ctx) + immutable_metadata.validate_decompress(plugin_ctx, common, asset_ctx) } Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_decompress(ctx) + verified_creators.validate_decompress(plugin_ctx, common, asset_ctx) + } + Plugin::Autograph(autograph) => { + autograph.validate_decompress(plugin_ctx, common, asset_ctx) } - Plugin::Autograph(autograph) => autograph.validate_decompress(ctx), } } /// Validate the add external plugin adapter lifecycle event. pub(crate) fn validate_add_external_plugin_adapter( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_add_external_plugin_adapter(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_add_external_plugin_adapter(ctx), - Plugin::BurnDelegate(burn) => burn.validate_add_external_plugin_adapter(ctx), + Plugin::Royalties(royalties) => { + royalties.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) + } + Plugin::FreezeDelegate(freeze) => { + freeze.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) + } + Plugin::BurnDelegate(burn) => { + burn.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) + } Plugin::TransferDelegate(transfer) => { - transfer.validate_add_external_plugin_adapter(ctx) + transfer.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } Plugin::UpdateDelegate(update_delegate) => { - update_delegate.validate_add_external_plugin_adapter(ctx) + update_delegate.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_add_external_plugin_adapter(ctx) + permanent_freeze.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => attributes.validate_add_external_plugin_adapter(ctx), - Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_add_external_plugin_adapter(ctx) + Plugin::Attributes(attributes) => { + attributes.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } + Plugin::PermanentTransferDelegate(permanent_transfer) => permanent_transfer + .validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx), Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_add_external_plugin_adapter(ctx) + permanent_burn.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) + } + Plugin::Edition(edition) => { + edition.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::Edition(edition) => edition.validate_add_external_plugin_adapter(ctx), Plugin::MasterEdition(master_edition) => { - master_edition.validate_add_external_plugin_adapter(ctx) + master_edition.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } Plugin::AddBlocker(add_blocker) => { - add_blocker.validate_add_external_plugin_adapter(ctx) - } - Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_add_external_plugin_adapter(ctx) + add_blocker.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_add_external_plugin_adapter(ctx) + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata + .validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::VerifiedCreators(verified_creators) => verified_creators + .validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::Autograph(autograph) => { + autograph.validate_add_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::Autograph(autograph) => autograph.validate_add_external_plugin_adapter(ctx), } } /// Validate the remove plugin lifecycle event. pub(crate) fn validate_remove_external_plugin_adapter( plugin: &Plugin, - ctx: &PluginValidationContext, + plugin_ctx: &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result { match plugin { - Plugin::Royalties(royalties) => royalties.validate_remove_external_plugin_adapter(ctx), - Plugin::FreezeDelegate(freeze) => freeze.validate_remove_external_plugin_adapter(ctx), - Plugin::BurnDelegate(burn) => burn.validate_remove_external_plugin_adapter(ctx), - Plugin::TransferDelegate(transfer) => { - transfer.validate_remove_external_plugin_adapter(ctx) - } - Plugin::UpdateDelegate(update_delegate) => { - update_delegate.validate_remove_external_plugin_adapter(ctx) + Plugin::Royalties(royalties) => { + royalties.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::PermanentFreezeDelegate(permanent_freeze) => { - permanent_freeze.validate_remove_external_plugin_adapter(ctx) + Plugin::FreezeDelegate(freeze) => { + freeze.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::Attributes(attributes) => { - attributes.validate_remove_external_plugin_adapter(ctx) + Plugin::BurnDelegate(burn) => { + burn.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::PermanentTransferDelegate(permanent_transfer) => { - permanent_transfer.validate_remove_external_plugin_adapter(ctx) + Plugin::TransferDelegate(transfer) => { + transfer.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::PermanentBurnDelegate(permanent_burn) => { - permanent_burn.validate_remove_external_plugin_adapter(ctx) + Plugin::UpdateDelegate(update_delegate) => update_delegate + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::PermanentFreezeDelegate(permanent_freeze) => permanent_freeze + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::Attributes(attributes) => { + attributes.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::Edition(edition) => edition.validate_remove_external_plugin_adapter(ctx), - Plugin::MasterEdition(master_edition) => { - master_edition.validate_remove_external_plugin_adapter(ctx) + Plugin::PermanentTransferDelegate(permanent_transfer) => permanent_transfer + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::PermanentBurnDelegate(permanent_burn) => permanent_burn + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::Edition(edition) => { + edition.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } + Plugin::MasterEdition(master_edition) => master_edition + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), Plugin::AddBlocker(add_blocker) => { - add_blocker.validate_remove_external_plugin_adapter(ctx) + add_blocker.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::ImmutableMetadata(immutable_metadata) => { - immutable_metadata.validate_remove_external_plugin_adapter(ctx) + Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::VerifiedCreators(verified_creators) => verified_creators + .validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx), + Plugin::Autograph(autograph) => { + autograph.validate_remove_external_plugin_adapter(plugin_ctx, common, asset_ctx) } - Plugin::VerifiedCreators(verified_creators) => { - verified_creators.validate_remove_external_plugin_adapter(ctx) - } - Plugin::Autograph(autograph) => autograph.validate_remove_external_plugin_adapter(ctx), } } } @@ -808,28 +1167,79 @@ impl From for ValidationResult { } /// The required context for a plugin validation. -#[allow(dead_code)] -pub(crate) struct PluginValidationContext<'a, 'b> { - /// This list of all the accounts passed into the instruction. - pub accounts: &'a [AccountInfo<'a>], - /// The asset account. - pub asset_info: Option<&'a AccountInfo<'a>>, - /// The collection account. - pub collection_info: Option<&'a AccountInfo<'a>>, +pub(crate) struct PluginValidationContext<'b> { + // /// This list of all the accounts passed into the instruction. + // pub accounts: &'a [AccountInfo<'a>], + // /// The asset account. + // pub asset_info: Option<&'a AccountInfo<'a>>, + // /// The collection account. + // pub collection_info: Option<&'a AccountInfo<'a>>, /// The authority of the current (self) plugin pub self_authority: &'b Authority, - /// The authority account info of ix `authority` signer - pub authority_info: &'a AccountInfo<'a>, + // /// The authority account info of ix `authority` signer + // pub authority_info: &'a AccountInfo<'a>, /// The authorities types which match the authority signer pub resolved_authorities: Option<&'b [Authority]>, - /// The new owner account for transfers - pub new_owner: Option<&'a AccountInfo<'a>>, - /// The new asset authority address. - pub new_asset_authority: Option<&'b UpdateAuthority>, - /// The new collection authority address. - pub new_collection_authority: Option<&'b Pubkey>, - /// The plugin being acted upon with new data from the ix if any. This None for create. - pub target_plugin: Option<&'b Plugin>, + // /// The new owner account for transfers + // pub new_owner: Option<&'a AccountInfo<'a>>, + // /// The new asset authority address. + // pub new_asset_authority: Option<&'b UpdateAuthority>, + // /// The new collection authority address. + // pub new_collection_authority: Option<&'b Pubkey>, + // /// The plugin being acted upon with new data from the ix if any. This None for create. + // pub target_plugin: Option<&'b Plugin>, +} + +/// The common context for a plugin validation. +pub(crate) struct AssetValidationCommon<'a> { + // pub(crate) accounts: &'a [AccountInfo<'a>], + pub(crate) authority_info: &'a AccountInfo<'a>, + pub(crate) asset_info: &'a AccountInfo<'a>, + pub(crate) collection_info: Option<&'a AccountInfo<'a>>, +} + +/// The required context for a plugin validation. +pub(crate) enum AssetValidationContext<'a> { + /// Add External Plugin Adapter Context + AddExternalPluginAdapter { + new_external_plugin_adapter: ExternalPluginAdapter, + }, + /// Add Plugin Context + AddPlugin { new_plugin: Plugin }, + /// Approve Plugin Authority Context + ApprovePluginAuthority { plugin: Plugin }, + /// Burn Context + Burn { accounts: &'a [AccountInfo<'a>] }, + /// Compress Context + Compress, + /// Create Context + Create { accounts: &'a [AccountInfo<'a>] }, + /// Decompress Context + Decompress, + /// Remove External Plugin Adapter Context + RemoveExternalPluginAdapter { + plugin_to_remove: ExternalPluginAdapter, + }, + /// Remove Plugin Context + RemovePlugin { plugin_to_remove: Plugin }, + /// Revoke Plugin Authority Context + RevokePluginAuthority { plugin: Plugin }, + /// Transfer Context + Transfer { + new_owner: &'a AccountInfo<'a>, + accounts: &'a [AccountInfo<'a>], + }, + // /// Update External Plugin Adapter Context + UpdateExternalPluginAdapter { + new_external_plugin_adapter: ExternalPluginAdapterUpdateInfo, + }, + /// Update Plugin Context + UpdatePlugin { new_plugin: Plugin }, + /// Update Context + Update { + new_update_authority: Option, + accounts: &'a [AccountInfo<'a>], + }, } /// Plugin validation trait which is implemented by each plugin. @@ -838,7 +1248,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when a new plugin is added. fn validate_add_plugin( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -847,7 +1259,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when the target plugin is removed. fn validate_remove_plugin( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -856,7 +1270,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when a new external plugin is added. fn validate_add_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -865,7 +1281,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when a new external plugin is removed. fn validate_remove_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -873,7 +1291,9 @@ pub(crate) trait PluginValidation { /// Validate the approve plugin authority lifecycle action. fn validate_approve_plugin_authority( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -881,7 +1301,9 @@ pub(crate) trait PluginValidation { /// Validate the revoke plugin authority lifecycle action. fn validate_revoke_plugin_authority( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -890,7 +1312,9 @@ pub(crate) trait PluginValidation { /// This ONLY gets called to validate the self plugin fn validate_create( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -899,7 +1323,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when an asset or collection is updated. fn validate_update( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -908,7 +1334,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when a plugin is updated. fn validate_update_plugin( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -917,7 +1345,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when an asset is burned. fn validate_burn( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -926,7 +1356,9 @@ pub(crate) trait PluginValidation { /// This gets called on all existing plugins when an asset is transferred. fn validate_transfer( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -934,7 +1366,9 @@ pub(crate) trait PluginValidation { /// Validate the compress lifecycle action. fn validate_compress( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -942,7 +1376,9 @@ pub(crate) trait PluginValidation { /// Validate the decompress lifecycle action. fn validate_decompress( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -950,7 +1386,9 @@ pub(crate) trait PluginValidation { /// Validate the update_plugin lifecycle action. fn validate_update_external_plugin_adapter( &self, - _ctx: &PluginValidationContext, + _plugin_ctx: &PluginValidationContext, + _common: &AssetValidationCommon, + _asset_ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -960,21 +1398,25 @@ pub(crate) trait PluginValidation { /// by deserializing and calling validate on the plugin. /// The STRONGEST result is returned. #[allow(clippy::too_many_arguments, clippy::type_complexity)] -pub(crate) fn validate_plugin_checks<'a>( +pub(crate) fn validate_plugin_checks<'a, 'b>( key: Key, - accounts: &'a [AccountInfo<'a>], + // accounts: &'a [AccountInfo<'a>], checks: &BTreeMap, - authority: &'a AccountInfo<'a>, - new_owner: Option<&'a AccountInfo<'a>>, - new_asset_authority: Option<&UpdateAuthority>, - new_collection_authority: Option<&Pubkey>, - new_plugin: Option<&Plugin>, - asset: Option<&'a AccountInfo<'a>>, - collection: Option<&'a AccountInfo<'a>>, + // authority: &'a AccountInfo<'a>, + // new_owner: Option<&'a AccountInfo<'a>>, + // new_asset_authority: Option<&UpdateAuthority>, + // new_collection_authority: Option<&Pubkey>, + // new_plugin: Option<&Plugin>, + // asset: Option<&'a AccountInfo<'a>>, + // collection: Option<&'a AccountInfo<'a>>, + common: &'b AssetValidationCommon<'a>, + ctx: &'b AssetValidationContext<'a>, resolved_authorities: &[Authority], plugin_validate_fp: fn( &Plugin, &PluginValidationContext, + &AssetValidationCommon<'a>, + &AssetValidationContext<'a>, ) -> Result, ) -> Result { let mut approved = false; @@ -987,27 +1429,35 @@ pub(crate) fn validate_plugin_checks<'a>( ) { let account = match key { - Key::CollectionV1 => collection.ok_or(MplCoreError::InvalidCollection)?, - Key::AssetV1 => asset.ok_or(MplCoreError::InvalidAsset)?, + Key::CollectionV1 => match load_key(common.asset_info, 0)? { + Key::AssetV1 => common + .collection_info + .ok_or(MplCoreError::InvalidCollection)?, + Key::CollectionV1 => common.asset_info, + _ => unreachable!(), + }, + Key::AssetV1 => common.asset_info, _ => unreachable!(), }; let validation_ctx = PluginValidationContext { - accounts, - asset_info: asset, - collection_info: collection, + // accounts, + // asset_info: asset, + // collection_info: collection, self_authority: ®istry_record.authority, - authority_info: authority, + // authority_info: authority, resolved_authorities: Some(resolved_authorities), - new_owner, - new_asset_authority, - new_collection_authority, - target_plugin: new_plugin, + // new_owner, + // new_asset_authority, + // new_collection_authority, + // target_plugin: new_plugin, }; let result = plugin_validate_fp( &Plugin::load(account, registry_record.offset)?, &validation_ctx, + common, + ctx, )?; match result { ValidationResult::Rejected => rejected = true, @@ -1031,24 +1481,28 @@ pub(crate) fn validate_plugin_checks<'a>( /// by deserializing and calling validate on the plugin. /// The STRONGEST result is returned. #[allow(clippy::too_many_arguments, clippy::type_complexity)] -pub(crate) fn validate_external_plugin_adapter_checks<'a>( +pub(crate) fn validate_external_plugin_adapter_checks( key: Key, - accounts: &'a [AccountInfo<'a>], + // accounts: &'a [AccountInfo<'a>], external_checks: &BTreeMap< ExternalPluginAdapterKey, (Key, ExternalCheckResultBits, ExternalRegistryRecord), >, - authority: &'a AccountInfo<'a>, - new_owner: Option<&'a AccountInfo<'a>>, - new_asset_authority: Option<&UpdateAuthority>, - new_collection_authority: Option<&Pubkey>, - new_plugin: Option<&Plugin>, - asset: Option<&'a AccountInfo<'a>>, - collection: Option<&'a AccountInfo<'a>>, + // authority: &'a AccountInfo<'a>, + // new_owner: Option<&'a AccountInfo<'a>>, + // new_asset_authority: Option<&UpdateAuthority>, + // new_collection_authority: Option<&Pubkey>, + // new_plugin: Option<&Plugin>, + // asset: Option<&'a AccountInfo<'a>>, + // collection: Option<&'a AccountInfo<'a>>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, resolved_authorities: &[Authority], external_plugin_adapter_validate_fp: fn( &ExternalPluginAdapter, &PluginValidationContext, + common: &AssetValidationCommon, + asset_ctx: &AssetValidationContext, ) -> Result, ) -> Result { let mut approved = false; @@ -1059,27 +1513,31 @@ pub(crate) fn validate_external_plugin_adapter_checks<'a>( || check_result.can_reject()) { let account = match key { - Key::CollectionV1 => collection.ok_or(MplCoreError::InvalidCollection)?, - Key::AssetV1 => asset.ok_or(MplCoreError::InvalidAsset)?, + Key::CollectionV1 => common + .collection_info + .ok_or(MplCoreError::InvalidCollection)?, + Key::AssetV1 => common.asset_info, _ => unreachable!(), }; let validation_ctx = PluginValidationContext { - accounts, - asset_info: asset, - collection_info: collection, + // accounts, + // asset_info: asset, + // collection_info: collection, self_authority: &external_registry_record.authority, - authority_info: authority, + // authority_info: authority, resolved_authorities: Some(resolved_authorities), - new_owner, - new_asset_authority, - new_collection_authority, - target_plugin: new_plugin, + // new_owner, + // new_asset_authority, + // new_collection_authority, + // target_plugin: new_plugin, }; let result = external_plugin_adapter_validate_fp( &ExternalPluginAdapter::load(account, external_registry_record.offset)?, &validation_ctx, + common, + ctx, )?; match result { ValidationResult::Rejected => { 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 8d9e0aa0..4db4c09b 100644 --- a/programs/mpl-core/src/processor/add_external_plugin_adapter.rs +++ b/programs/mpl-core/src/processor/add_external_plugin_adapter.rs @@ -8,9 +8,9 @@ use crate::{ AddCollectionExternalPluginAdapterV1Accounts, AddExternalPluginAdapterV1Accounts, }, plugins::{ - create_meta_idempotent, initialize_external_plugin_adapter, ExternalPluginAdapter, - ExternalPluginAdapterInitInfo, Plugin, PluginType, PluginValidationContext, - ValidationResult, + create_meta_idempotent, initialize_external_plugin_adapter, AssetValidationCommon, + AssetValidationContext, ExternalPluginAdapter, ExternalPluginAdapterInitInfo, Plugin, + PluginType, PluginValidationContext, ValidationResult, }, state::{AssetV1, Authority, CollectionV1, DataBlob, Key, SolanaAccount}, utils::{ @@ -63,21 +63,32 @@ pub(crate) fn add_external_plugin_adapter<'a>( } let validation_ctx = PluginValidationContext { - accounts, - asset_info: Some(ctx.accounts.asset), - collection_info: ctx.accounts.collection, + // accounts, + // asset_info: Some(ctx.accounts.asset), + // collection_info: ctx.accounts.collection, self_authority: &Authority::UpdateAuthority, - authority_info: authority, + // authority_info: authority, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, }; + let external_plugin_adapter = ExternalPluginAdapter::from(&args.init_info); + if ExternalPluginAdapter::validate_add_external_plugin_adapter( &ExternalPluginAdapter::from(&args.init_info), &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::AddExternalPluginAdapter { + new_external_plugin_adapter: external_plugin_adapter, + }, )? == ValidationResult::Rejected { return Err(MplCoreError::InvalidAuthority.into()); @@ -85,16 +96,29 @@ pub(crate) fn add_external_plugin_adapter<'a>( let external_plugin_adapter = ExternalPluginAdapter::from(&args.init_info); + let common = AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }; + + let asset_ctx = AssetValidationContext::AddExternalPluginAdapter { + new_external_plugin_adapter: external_plugin_adapter, + }; + // Validate asset permissions. let (mut asset, _, _) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - Some(&external_plugin_adapter), + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // Some(&external_plugin_adapter), + &common, + &asset_ctx, AssetV1::check_add_external_plugin_adapter, CollectionV1::check_add_external_plugin_adapter, PluginType::check_add_external_plugin_adapter, @@ -148,36 +172,58 @@ pub(crate) fn add_collection_external_plugin_adapter<'a>( } let validation_ctx = PluginValidationContext { - accounts, - asset_info: None, - collection_info: Some(ctx.accounts.collection), + // accounts, + // asset_info: None, + // collection_info: Some(ctx.accounts.collection), self_authority: &Authority::UpdateAuthority, - authority_info: authority, + // authority_info: authority, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, }; + let external_plugin_adapter = ExternalPluginAdapter::from(&args.init_info); + if ExternalPluginAdapter::validate_add_external_plugin_adapter( &ExternalPluginAdapter::from(&args.init_info), &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::AddExternalPluginAdapter { + new_external_plugin_adapter: external_plugin_adapter.clone(), + }, )? == ValidationResult::Rejected { return Err(MplCoreError::InvalidAuthority.into()); } - let external_plugin_adapter = ExternalPluginAdapter::from(&args.init_info); + let common = AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }; + + let asset_ctx = AssetValidationContext::AddExternalPluginAdapter { + new_external_plugin_adapter: external_plugin_adapter, + }; // Validate collection permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - None, - Some(&external_plugin_adapter), + // accounts, + // authority, + // ctx.accounts.collection, + // None, + // None, + // Some(&external_plugin_adapter), + &common, + &asset_ctx, CollectionV1::check_add_external_plugin_adapter, PluginType::check_add_external_plugin_adapter, CollectionV1::validate_add_external_plugin_adapter, diff --git a/programs/mpl-core/src/processor/add_plugin.rs b/programs/mpl-core/src/processor/add_plugin.rs index cff3a3b3..ddb4c844 100644 --- a/programs/mpl-core/src/processor/add_plugin.rs +++ b/programs/mpl-core/src/processor/add_plugin.rs @@ -6,8 +6,8 @@ use crate::{ error::MplCoreError, instruction::accounts::{AddCollectionPluginV1Accounts, AddPluginV1Accounts}, plugins::{ - create_meta_idempotent, initialize_plugin, Plugin, PluginType, PluginValidationContext, - ValidationResult, + create_meta_idempotent, initialize_plugin, AssetValidationCommon, AssetValidationContext, + Plugin, PluginType, PluginValidationContext, ValidationResult, }, state::{AssetV1, Authority, CollectionV1, DataBlob, Key, SolanaAccount}, utils::{ @@ -56,31 +56,53 @@ pub(crate) fn add_plugin<'a>( // TODO: Seed with Rejected // TODO: refactor to allow add_plugin to approve additions let validation_ctx = PluginValidationContext { - accounts, - asset_info: Some(ctx.accounts.asset), - collection_info: ctx.accounts.collection, + // accounts, + // asset_info: Some(ctx.accounts.asset), + // collection_info: ctx.accounts.collection, self_authority: &args.init_authority.unwrap_or(args.plugin.manager()), - authority_info: authority, + // authority_info: authority, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: Some(&args.plugin), + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: Some(&args.plugin), }; - if Plugin::validate_add_plugin(&args.plugin, &validation_ctx)? == ValidationResult::Rejected { + if Plugin::validate_add_plugin( + &args.plugin, + &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::AddPlugin { + new_plugin: args.plugin.clone(), + }, + )? == ValidationResult::Rejected + { return Err(MplCoreError::InvalidAuthority.into()); } // Validate asset permissions. let (mut asset, _, _) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - Some(&args.plugin), - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // Some(&args.plugin), + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::AddPlugin { + new_plugin: args.plugin.clone(), + }, AssetV1::check_add_plugin, CollectionV1::check_add_plugin, PluginType::check_add_plugin, @@ -131,18 +153,31 @@ pub(crate) fn add_collection_plugin<'a>( } let validation_ctx = PluginValidationContext { - accounts, - asset_info: None, - collection_info: Some(ctx.accounts.collection), + // // accounts, + // // asset_info: None, + // // collection_info: Some(ctx.accounts.collection), self_authority: &args.init_authority.unwrap_or(args.plugin.manager()), - authority_info: authority, + // // authority_info: authority, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: Some(&args.plugin), + // // new_owner: None, + // // new_asset_authority: None, + // // new_collection_authority: None, + // // target_plugin: Some(&args.plugin), }; - if Plugin::validate_add_plugin(&args.plugin, &validation_ctx)? == ValidationResult::Rejected { + if Plugin::validate_add_plugin( + &args.plugin, + &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::AddPlugin { + new_plugin: args.plugin.clone(), + }, + )? == ValidationResult::Rejected + { return Err(MplCoreError::InvalidAuthority.into()); } @@ -153,12 +188,21 @@ pub(crate) fn add_collection_plugin<'a>( // Validate collection permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - Some(&args.plugin), - None, + // accounts, + // authority, + // ctx.accounts.collection, + // None, + // Some(&args.plugin), + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::AddPlugin { + new_plugin: args.plugin.clone(), + }, CollectionV1::check_add_plugin, PluginType::check_add_plugin, CollectionV1::validate_add_plugin, diff --git a/programs/mpl-core/src/processor/approve_plugin_authority.rs b/programs/mpl-core/src/processor/approve_plugin_authority.rs index 1486b38a..4491636d 100644 --- a/programs/mpl-core/src/processor/approve_plugin_authority.rs +++ b/programs/mpl-core/src/processor/approve_plugin_authority.rs @@ -7,7 +7,10 @@ use crate::{ instruction::accounts::{ ApproveCollectionPluginAuthorityV1Accounts, ApprovePluginAuthorityV1Accounts, }, - plugins::{approve_authority_on_plugin, fetch_wrapped_plugin, Plugin, PluginType}, + plugins::{ + approve_authority_on_plugin, fetch_wrapped_plugin, AssetValidationCommon, + AssetValidationContext, Plugin, PluginType, + }, state::{AssetV1, Authority, CollectionV1, CoreAsset, DataBlob, Key, SolanaAccount}, utils::{ fetch_core_data, load_key, resolve_authority, validate_asset_permissions, @@ -49,16 +52,27 @@ pub(crate) fn approve_plugin_authority<'a>( let (_, plugin) = fetch_wrapped_plugin::(ctx.accounts.asset, None, args.plugin_type)?; + let common = AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }; + + let asset_ctx = AssetValidationContext::ApprovePluginAuthority { plugin }; + // Validate asset permissions. let (mut asset, _, _) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - Some(&plugin), - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // Some(&plugin), + // None, + &common, + &asset_ctx, AssetV1::check_approve_plugin_authority, CollectionV1::check_approve_plugin_authority, PluginType::check_approve_plugin_authority, @@ -113,12 +127,19 @@ pub(crate) fn approve_collection_plugin_authority<'a>( // Validate collection permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - Some(&plugin), - None, + // accounts, + // authority, + // ctx.accounts.collection, + // None, + // Some(&plugin), + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::ApprovePluginAuthority { plugin }, CollectionV1::check_approve_plugin_authority, PluginType::check_approve_plugin_authority, CollectionV1::validate_approve_plugin_authority, diff --git a/programs/mpl-core/src/processor/burn.rs b/programs/mpl-core/src/processor/burn.rs index 0fad2e53..42b292c3 100644 --- a/programs/mpl-core/src/processor/burn.rs +++ b/programs/mpl-core/src/processor/burn.rs @@ -5,7 +5,10 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::{BurnCollectionV1Accounts, BurnV1Accounts}, - plugins::{ExternalPluginAdapter, HookableLifecycleEvent, Plugin, PluginType}, + plugins::{ + AssetValidationCommon, AssetValidationContext, ExternalPluginAdapter, + HookableLifecycleEvent, Plugin, PluginType, + }, state::{AssetV1, CollectionV1, CompressionProof, Key, SolanaAccount, Wrappable}, utils::{ close_program_account, load_key, rebuild_account_state_from_proof_data, resolve_authority, @@ -84,14 +87,23 @@ pub(crate) fn burn<'a>(accounts: &'a [AccountInfo<'a>], args: BurnV1Args) -> Pro // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // None, + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Burn { accounts }, AssetV1::check_burn, CollectionV1::check_burn, PluginType::check_burn, diff --git a/programs/mpl-core/src/processor/compress.rs b/programs/mpl-core/src/processor/compress.rs index 893a93b3..ba0b9b4f 100644 --- a/programs/mpl-core/src/processor/compress.rs +++ b/programs/mpl-core/src/processor/compress.rs @@ -5,7 +5,7 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::CompressV1Accounts, - plugins::{Plugin, PluginType}, + plugins::{AssetValidationCommon, AssetValidationContext, Plugin, PluginType}, state::{AssetV1, CollectionV1, Key, Wrappable}, utils::{ compress_into_account_space, fetch_core_data, load_key, resolve_authority, @@ -44,14 +44,23 @@ pub(crate) fn compress<'a>( // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // None, + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Compress, AssetV1::check_compress, CollectionV1::check_compress, PluginType::check_compress, diff --git a/programs/mpl-core/src/processor/create.rs b/programs/mpl-core/src/processor/create.rs index bc14f00f..2c61b0d8 100644 --- a/programs/mpl-core/src/processor/create.rs +++ b/programs/mpl-core/src/processor/create.rs @@ -10,9 +10,10 @@ use crate::{ instruction::accounts::CreateV2Accounts, plugins::{ create_meta_idempotent, create_plugin_meta, initialize_external_plugin_adapter, - initialize_plugin, CheckResult, ExternalCheckResultBits, ExternalPluginAdapter, - ExternalPluginAdapterInitInfo, HookableLifecycleEvent, Plugin, PluginAuthorityPair, - PluginType, PluginValidationContext, ValidationResult, + initialize_plugin, AssetValidationCommon, AssetValidationContext, CheckResult, + ExternalCheckResultBits, ExternalPluginAdapter, ExternalPluginAdapterInitInfo, + HookableLifecycleEvent, Plugin, PluginAuthorityPair, PluginType, PluginValidationContext, + ValidationResult, }, state::{ get_create_fee, AssetV1, Authority, CollectionV1, DataState, SolanaAccount, UpdateAuthority, @@ -149,14 +150,21 @@ pub(crate) fn process_create<'a>( if args.data_state == DataState::AccountState { // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Create { accounts }, AssetV1::check_create, CollectionV1::check_create, PluginType::check_create, @@ -190,18 +198,28 @@ pub(crate) fn process_create<'a>( != CheckResult::None { let validation_ctx = PluginValidationContext { - accounts, - asset_info: Some(ctx.accounts.asset), - collection_info: ctx.accounts.collection, + // // accounts, + // // asset_info: Some(ctx.accounts.asset), + // // collection_info: ctx.accounts.collection, self_authority: &plugin.authority.unwrap_or(plugin.plugin.manager()), - authority_info: authority, + // // authority_info: authority, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, + // // new_owner: None, + // // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, }; - match Plugin::validate_create(&plugin.plugin, &validation_ctx)? { + match Plugin::validate_create( + &plugin.plugin, + &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Create { accounts }, + )? { ValidationResult::Rejected => approved = false, ValidationResult::ForceApproved => force_approved = true, _ => (), @@ -247,22 +265,32 @@ pub(crate) fn process_create<'a>( } if external_check_result_bits.can_reject() { - let validation_ctx = PluginValidationContext { - accounts, - asset_info: Some(ctx.accounts.asset), - collection_info: ctx.accounts.collection, - // External plugin adapters are always managed by the update authority. - self_authority: &Authority::UpdateAuthority, - authority_info: authority, - resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, - }; + // let validation_ctx = PluginValidationContext { + // accounts, + // asset_info: Some(ctx.accounts.asset), + // collection_info: ctx.accounts.collection, + // // External plugin adapters are always managed by the update authority. + // self_authority: &Authority::UpdateAuthority, + // authority_info: authority, + // resolved_authorities: None, + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, + // }; if ExternalPluginAdapter::validate_create( &ExternalPluginAdapter::from(plugin_init_info), - &validation_ctx, + &PluginValidationContext { + self_authority: &Authority::UpdateAuthority, + resolved_authorities: None, + }, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Create { accounts }, )? == ValidationResult::Rejected { approved = false; diff --git a/programs/mpl-core/src/processor/create_collection.rs b/programs/mpl-core/src/processor/create_collection.rs index 2a14cfb4..928b0ab6 100644 --- a/programs/mpl-core/src/processor/create_collection.rs +++ b/programs/mpl-core/src/processor/create_collection.rs @@ -10,8 +10,9 @@ use crate::{ instruction::accounts::CreateCollectionV2Accounts, plugins::{ create_meta_idempotent, create_plugin_meta, initialize_external_plugin_adapter, - initialize_plugin, CheckResult, ExternalPluginAdapterInitInfo, Plugin, PluginAuthorityPair, - PluginType, PluginValidationContext, ValidationResult, + initialize_plugin, AssetValidationCommon, AssetValidationContext, CheckResult, + ExternalPluginAdapterInitInfo, Plugin, PluginAuthorityPair, PluginType, + PluginValidationContext, ValidationResult, }, state::{Authority, CollectionV1, Key}, }; @@ -134,18 +135,28 @@ pub(crate) fn process_create_collection<'a>( if PluginType::check_create(&plugin_type) != CheckResult::None { let validation_ctx = PluginValidationContext { - accounts, - asset_info: None, - collection_info: Some(ctx.accounts.collection), + // // accounts, + // // asset_info: None, + // // collection_info: Some(ctx.accounts.collection), self_authority: &plugin.authority.unwrap_or(plugin.plugin.manager()), - authority_info: ctx.accounts.payer, + // // authority_info: ctx.accounts.payer, resolved_authorities: None, - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, + // // new_owner: None, + // // new_asset_authority: None, + // // new_collection_authority: None, + // // target_plugin: None, }; - match Plugin::validate_create(&plugin.plugin, &validation_ctx)? { + match Plugin::validate_create( + &plugin.plugin, + &validation_ctx, + &AssetValidationCommon { + // accounts, + authority_info: ctx.accounts.payer, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::Create { accounts }, + )? { ValidationResult::Rejected => approved = false, ValidationResult::ForceApproved => force_approved = true, _ => (), diff --git a/programs/mpl-core/src/processor/decompress.rs b/programs/mpl-core/src/processor/decompress.rs index a9bd17df..4aeb7989 100644 --- a/programs/mpl-core/src/processor/decompress.rs +++ b/programs/mpl-core/src/processor/decompress.rs @@ -5,7 +5,7 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg, use crate::{ error::MplCoreError, instruction::accounts::DecompressV1Accounts, - plugins::{Plugin, PluginType}, + plugins::{AssetValidationCommon, AssetValidationContext, Plugin, PluginType}, state::{AssetV1, CollectionV1, CompressionProof, Key}, utils::{ load_key, rebuild_account_state_from_proof_data, resolve_authority, @@ -60,14 +60,22 @@ pub(crate) fn decompress<'a>( // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Decompress, AssetV1::check_decompress, CollectionV1::check_decompress, PluginType::check_decompress, diff --git a/programs/mpl-core/src/processor/remove_external_plugin_adapter.rs b/programs/mpl-core/src/processor/remove_external_plugin_adapter.rs index f5f5f253..1c935055 100644 --- a/programs/mpl-core/src/processor/remove_external_plugin_adapter.rs +++ b/programs/mpl-core/src/processor/remove_external_plugin_adapter.rs @@ -9,7 +9,8 @@ use crate::{ }, plugins::{ delete_external_plugin_adapter, fetch_wrapped_external_plugin_adapter, - ExternalPluginAdapterKey, Plugin, PluginType, + AssetValidationCommon, AssetValidationContext, ExternalPluginAdapterKey, Plugin, + PluginType, }, state::{AssetV1, CollectionV1, DataBlob, Key}, utils::{ @@ -65,14 +66,21 @@ pub(crate) fn remove_external_plugin_adapter<'a>( // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - None, - Some(&plugin_to_remove), + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // None, + // Some(&plugin_to_remove), + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::RemoveExternalPluginAdapter { plugin_to_remove }, AssetV1::check_remove_external_plugin_adapter, CollectionV1::check_remove_external_plugin_adapter, PluginType::check_remove_external_plugin_adapter, @@ -135,12 +143,13 @@ pub(crate) fn remove_collection_external_plugin_adapter<'a>( // Validate asset permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - None, - Some(&plugin_to_remove), + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::RemoveExternalPluginAdapter { plugin_to_remove }, CollectionV1::check_remove_external_plugin_adapter, PluginType::check_remove_external_plugin_adapter, CollectionV1::validate_remove_external_plugin_adapter, diff --git a/programs/mpl-core/src/processor/remove_plugin.rs b/programs/mpl-core/src/processor/remove_plugin.rs index 83efdd9c..6f6145a6 100644 --- a/programs/mpl-core/src/processor/remove_plugin.rs +++ b/programs/mpl-core/src/processor/remove_plugin.rs @@ -5,7 +5,10 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::{RemoveCollectionPluginV1Accounts, RemovePluginV1Accounts}, - plugins::{delete_plugin, fetch_wrapped_plugin, Plugin, PluginType}, + plugins::{ + delete_plugin, fetch_wrapped_plugin, AssetValidationCommon, AssetValidationContext, Plugin, + PluginType, + }, state::{AssetV1, CollectionV1, DataBlob, Key}, utils::{ fetch_core_data, load_key, resolve_authority, validate_asset_permissions, @@ -57,14 +60,20 @@ pub(crate) fn remove_plugin<'a>( // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - Some(&plugin_to_remove), - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // Some(&plugin_to_remove), + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::RemovePlugin { plugin_to_remove }, AssetV1::check_remove_plugin, CollectionV1::check_remove_plugin, PluginType::check_remove_plugin, @@ -129,12 +138,13 @@ pub(crate) fn remove_collection_plugin<'a>( // Validate collection permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - Some(&plugin_to_remove), - None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::RemovePlugin { plugin_to_remove }, CollectionV1::check_remove_plugin, PluginType::check_remove_plugin, CollectionV1::validate_remove_plugin, diff --git a/programs/mpl-core/src/processor/revoke_plugin_authority.rs b/programs/mpl-core/src/processor/revoke_plugin_authority.rs index f68cf57c..1df83795 100644 --- a/programs/mpl-core/src/processor/revoke_plugin_authority.rs +++ b/programs/mpl-core/src/processor/revoke_plugin_authority.rs @@ -8,8 +8,8 @@ use crate::{ RevokeCollectionPluginAuthorityV1Accounts, RevokePluginAuthorityV1Accounts, }, plugins::{ - fetch_wrapped_plugin, revoke_authority_on_plugin, Plugin, PluginHeaderV1, PluginRegistryV1, - PluginType, + fetch_wrapped_plugin, revoke_authority_on_plugin, AssetValidationCommon, + AssetValidationContext, Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, }, state::{AssetV1, CollectionV1, Key}, utils::{ @@ -58,14 +58,22 @@ pub(crate) fn revoke_plugin_authority<'a>( // Validate asset permissions. let _ = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - Some(&plugin), - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // Some(&plugin), + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::RevokePluginAuthority { + plugin: plugin.clone(), + }, AssetV1::check_revoke_plugin_authority, CollectionV1::check_revoke_plugin_authority, PluginType::check_revoke_plugin_authority, @@ -134,12 +142,15 @@ pub(crate) fn revoke_collection_plugin_authority<'a>( // Validate collection permissions. let _ = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - Some(&plugin), - None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::RevokePluginAuthority { + plugin: plugin.clone(), + }, CollectionV1::check_revoke_plugin_authority, PluginType::check_revoke_plugin_authority, CollectionV1::validate_revoke_plugin_authority, diff --git a/programs/mpl-core/src/processor/transfer.rs b/programs/mpl-core/src/processor/transfer.rs index d6c5f550..041f5153 100644 --- a/programs/mpl-core/src/processor/transfer.rs +++ b/programs/mpl-core/src/processor/transfer.rs @@ -5,7 +5,10 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::TransferV1Accounts, - plugins::{ExternalPluginAdapter, HookableLifecycleEvent, Plugin, PluginType}, + plugins::{ + AssetValidationCommon, AssetValidationContext, ExternalPluginAdapter, + HookableLifecycleEvent, Plugin, PluginType, + }, state::{AssetV1, Authority, CollectionV1, CompressionProof, Key, SolanaAccount, Wrappable}, utils::{ compress_into_account_space, load_key, rebuild_account_state_from_proof_data, @@ -77,14 +80,24 @@ pub(crate) fn transfer<'a>(accounts: &'a [AccountInfo<'a>], args: TransferV1Args // Validate asset permissions. let (mut asset, plugin_header, plugin_registry) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - Some(ctx.accounts.new_owner), - None, - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // Some(ctx.accounts.new_owner), + // None, + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Transfer { + accounts, + new_owner: ctx.accounts.new_owner, + }, AssetV1::check_transfer, CollectionV1::check_transfer, PluginType::check_transfer, diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index 23c6d9db..09479789 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -10,8 +10,9 @@ use crate::{ Context, UpdateCollectionV1Accounts, UpdateV1Accounts, UpdateV2Accounts, }, plugins::{ - fetch_plugin, ExternalPluginAdapter, HookableLifecycleEvent, Plugin, PluginHeaderV1, - PluginRegistryV1, PluginType, UpdateDelegate, + fetch_plugin, AssetValidationCommon, AssetValidationContext, ExternalPluginAdapter, + HookableLifecycleEvent, Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, + UpdateDelegate, }, state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount, UpdateAuthority}, utils::{ @@ -107,14 +108,24 @@ fn update<'a>( } let (mut asset, plugin_header, plugin_registry) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - args.new_update_authority.as_ref(), - None, - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // args.new_update_authority.as_ref(), + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::Update { + accounts, + new_update_authority: args.new_update_authority.clone(), + }, AssetV1::check_update, CollectionV1::check_update, PluginType::check_update, @@ -269,12 +280,25 @@ pub(crate) fn update_collection<'a>( } let (mut collection, plugin_header, plugin_registry) = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - ctx.accounts.new_update_authority.map(|a| a.key), - None, - None, + // accounts, + // authority, + // ctx.accounts.collection, + // ctx.accounts.new_update_authority.map(|a| a.key), + // None, + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::Update { + accounts, + new_update_authority: ctx + .accounts + .new_update_authority + .map(|a| UpdateAuthority::Address(*a.key)), + }, CollectionV1::check_update, PluginType::check_update, CollectionV1::validate_update, 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..bf93d79d 100644 --- a/programs/mpl-core/src/processor/update_external_plugin_adapter.rs +++ b/programs/mpl-core/src/processor/update_external_plugin_adapter.rs @@ -11,8 +11,9 @@ use crate::{ }, plugins::{ fetch_wrapped_external_plugin_adapter, find_external_plugin_adapter_mut, - ExternalPluginAdapter, ExternalPluginAdapterKey, ExternalPluginAdapterUpdateInfo, - PluginHeaderV1, PluginRegistryV1, PluginValidationContext, ValidationResult, + AssetValidationCommon, AssetValidationContext, ExternalPluginAdapter, + ExternalPluginAdapterKey, ExternalPluginAdapterUpdateInfo, PluginHeaderV1, + PluginRegistryV1, PluginValidationContext, ValidationResult, }, state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount}, utils::{ @@ -63,22 +64,34 @@ pub(crate) fn update_external_plugin_adapter<'a>( let (external_registry_record, external_plugin_adapter) = fetch_wrapped_external_plugin_adapter::(ctx.accounts.asset, None, &args.key)?; - let validation_ctx = PluginValidationContext { - accounts, - asset_info: Some(ctx.accounts.asset), - collection_info: ctx.accounts.collection, - self_authority: &external_registry_record.authority, - authority_info: authority, - resolved_authorities: Some(&resolved_authorities), - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, - }; + // let validation_ctx = PluginValidationContext { + // accounts, + // asset_info: Some(ctx.accounts.asset), + // collection_info: ctx.accounts.collection, + // self_authority: &external_registry_record.authority, + // authority_info: authority, + // resolved_authorities: Some(&resolved_authorities), + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, + // }; if ExternalPluginAdapter::validate_update_external_plugin_adapter( &external_plugin_adapter, - &validation_ctx, + &PluginValidationContext { + self_authority: &external_registry_record.authority, + resolved_authorities: Some(&resolved_authorities), + }, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::UpdateExternalPluginAdapter { + new_external_plugin_adapter: args.update_info.clone(), + }, )? != ValidationResult::Approved { return Err(MplCoreError::InvalidAuthority.into()); @@ -141,22 +154,34 @@ pub(crate) fn update_collection_external_plugin_adapter<'a>( &args.key, )?; - let validation_ctx = PluginValidationContext { - accounts, - asset_info: None, - collection_info: Some(ctx.accounts.collection), - self_authority: &external_registry_record.authority, - authority_info: authority, - resolved_authorities: Some(&resolved_authorities), - new_owner: None, - new_asset_authority: None, - new_collection_authority: None, - target_plugin: None, - }; + // let validation_ctx = PluginValidationContext { + // accounts, + // asset_info: None, + // collection_info: Some(ctx.accounts.collection), + // self_authority: &external_registry_record.authority, + // authority_info: authority, + // resolved_authorities: Some(&resolved_authorities), + // new_owner: None, + // new_asset_authority: None, + // new_collection_authority: None, + // target_plugin: None, + // }; if ExternalPluginAdapter::validate_update_external_plugin_adapter( &external_plugin_adapter, - &validation_ctx, + &PluginValidationContext { + self_authority: &external_registry_record.authority, + resolved_authorities: Some(&resolved_authorities), + }, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::UpdateExternalPluginAdapter { + new_external_plugin_adapter: args.update_info.clone(), + }, )? != ValidationResult::Approved { return Err(MplCoreError::InvalidAuthority.into()); diff --git a/programs/mpl-core/src/processor/update_plugin.rs b/programs/mpl-core/src/processor/update_plugin.rs index a4810f36..cc90f152 100644 --- a/programs/mpl-core/src/processor/update_plugin.rs +++ b/programs/mpl-core/src/processor/update_plugin.rs @@ -7,7 +7,10 @@ use solana_program::{ use crate::{ error::MplCoreError, instruction::accounts::{UpdateCollectionPluginV1Accounts, UpdatePluginV1Accounts}, - plugins::{Plugin, PluginHeaderV1, PluginRegistryV1, PluginType}, + plugins::{ + AssetValidationCommon, AssetValidationContext, Plugin, PluginHeaderV1, PluginRegistryV1, + PluginType, + }, state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount}, utils::{ load_key, resize_or_reallocate_account, resolve_authority, validate_asset_permissions, @@ -48,14 +51,22 @@ pub(crate) fn update_plugin<'a>( } let (mut asset, plugin_header, plugin_registry) = validate_asset_permissions( - accounts, - authority, - ctx.accounts.asset, - ctx.accounts.collection, - None, - None, - Some(&args.plugin), - None, + // accounts, + // authority, + // ctx.accounts.asset, + // ctx.accounts.collection, + // None, + // None, + // Some(&args.plugin), + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.asset, + collection_info: ctx.accounts.collection, + }, + &AssetValidationContext::UpdatePlugin { + new_plugin: args.plugin.clone(), + }, AssetV1::check_update_plugin, CollectionV1::check_update_plugin, PluginType::check_update_plugin, @@ -109,12 +120,21 @@ pub(crate) fn update_collection_plugin<'a>( // Validate collection permissions. let (collection, plugin_header, plugin_registry) = validate_collection_permissions( - accounts, - authority, - ctx.accounts.collection, - None, - Some(&args.plugin), - None, + // accounts, + // authority, + // ctx.accounts.collection, + // None, + // Some(&args.plugin), + // None, + &AssetValidationCommon { + // accounts, + authority_info: authority, + asset_info: ctx.accounts.collection, + collection_info: None, + }, + &AssetValidationContext::UpdatePlugin { + new_plugin: args.plugin.clone(), + }, CollectionV1::check_update_plugin, PluginType::check_update_plugin, CollectionV1::validate_update_plugin, diff --git a/programs/mpl-core/src/state/asset.rs b/programs/mpl-core/src/state/asset.rs index 30ec3c56..9aa1ceed 100644 --- a/programs/mpl-core/src/state/asset.rs +++ b/programs/mpl-core/src/state/asset.rs @@ -8,7 +8,10 @@ use std::mem::size_of; use crate::{ error::MplCoreError, - plugins::{abstain, approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{ + abstain, approve, AssetValidationCommon, AssetValidationContext, CheckResult, Plugin, + ValidationResult, + }, state::{Compressible, CompressionProof, DataBlob, Key, SolanaAccount}, }; @@ -136,9 +139,11 @@ impl AssetV1 { /// Validate the create lifecycle event. pub fn validate_create( &self, - _authority_info: &AccountInfo, - _new_plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _new_plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { // If the asset is part of a collection, the collection must approve the create. match self.update_authority { @@ -150,55 +155,61 @@ impl AssetV1 { /// Validate the add plugin lifecycle event. pub fn validate_add_plugin( &self, - authority_info: &AccountInfo, - new_plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // new_plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let new_plugin = match new_plugin { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - // If it's an owner managed plugin or a UA managed plugin and the asset - // is not in a collection, then it can be added. - if (authority_info.key == &self.owner && new_plugin.manager() == Authority::Owner) - || (UpdateAuthority::Address(*authority_info.key) == self.update_authority - && new_plugin.manager() == Authority::UpdateAuthority) - { - approve!() + if let AssetValidationContext::AddPlugin { new_plugin } = ctx { + // If it's an owner managed plugin or a UA managed plugin and the asset + // is not in a collection, then it can be added. + if (common.authority_info.key == &self.owner + && new_plugin.manager() == Authority::Owner) + || (UpdateAuthority::Address(*common.authority_info.key) == self.update_authority + && new_plugin.manager() == Authority::UpdateAuthority) + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the remove plugin lifecycle event. pub fn validate_remove_plugin( &self, - authority_info: &AccountInfo, - plugin_to_remove: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin_to_remove: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let plugin = match plugin_to_remove { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - if (plugin.manager() == Authority::UpdateAuthority - && self.update_authority == UpdateAuthority::Address(*authority_info.key)) - || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) - { - approve!() + if let AssetValidationContext::RemovePlugin { plugin_to_remove } = ctx { + if (plugin_to_remove.manager() == Authority::UpdateAuthority + && self.update_authority == UpdateAuthority::Address(*common.authority_info.key)) + || (plugin_to_remove.manager() == Authority::Owner + && common.authority_info.key == &self.owner) + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the update plugin lifecycle event. pub fn validate_update_plugin( &self, - _authority_info: &AccountInfo, - _plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { abstain!() } @@ -206,53 +217,61 @@ impl AssetV1 { /// Validate the approve plugin authority lifecycle event. pub fn validate_approve_plugin_authority( &self, - authority_info: &AccountInfo, - plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if let Some(plugin) = plugin { + if let AssetValidationContext::ApprovePluginAuthority { plugin } = ctx { if (plugin.manager() == Authority::UpdateAuthority - && self.update_authority == UpdateAuthority::Address(*authority_info.key)) - || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) + && self.update_authority == UpdateAuthority::Address(*common.authority_info.key)) + || (plugin.manager() == Authority::Owner + && common.authority_info.key == &self.owner) { approve!() } else { abstain!() } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the revoke plugin authority lifecycle event. pub fn validate_revoke_plugin_authority( &self, - authority_info: &AccountInfo, - plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if let Some(plugin) = plugin { + if let AssetValidationContext::RevokePluginAuthority { plugin } = ctx { if (plugin.manager() == Authority::UpdateAuthority - && self.update_authority == UpdateAuthority::Address(*authority_info.key)) - || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) + && self.update_authority == UpdateAuthority::Address(*common.authority_info.key)) + || (plugin.manager() == Authority::Owner + && common.authority_info.key == &self.owner) { approve!() } else { abstain!() } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the update lifecycle event. pub fn validate_update( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.update_authority.key() { + if common.authority_info.key == &self.update_authority.key() { approve!() } else { abstain!() @@ -262,11 +281,13 @@ impl AssetV1 { /// Validate the burn lifecycle event. pub fn validate_burn( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.owner { + if common.authority_info.key == &self.owner { approve!() } else { abstain!() @@ -276,11 +297,13 @@ impl AssetV1 { /// Validate the transfer lifecycle event. pub fn validate_transfer( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.owner { + if common.authority_info.key == &self.owner { approve!() } else { abstain!() @@ -290,11 +313,13 @@ impl AssetV1 { /// Validate the compress lifecycle event. pub fn validate_compress( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.owner { + if common.authority_info.key == &self.owner { approve!() } else { abstain!() @@ -304,11 +329,13 @@ impl AssetV1 { /// Validate the decompress lifecycle event. pub fn validate_decompress( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.owner { + if common.authority_info.key == &self.owner { approve!() } else { abstain!() @@ -318,12 +345,14 @@ impl AssetV1 { /// Validate the add external plugin adapter lifecycle event. pub fn validate_add_external_plugin_adapter( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _new_plugin: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _new_plugin: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { // If it's not in a collection, then it can be added. - if UpdateAuthority::Address(*authority_info.key) == self.update_authority { + if UpdateAuthority::Address(*common.authority_info.key) == self.update_authority { approve!() } else { abstain!() @@ -331,13 +360,15 @@ impl AssetV1 { } /// Validate the remove external plugin adapter lifecycle event. - pub fn validate_remove_external_plugin_adapter( + pub(crate) fn validate_remove_external_plugin_adapter( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _plugin_to_remove: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _plugin_to_remove: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { - if self.update_authority == UpdateAuthority::Address(*authority_info.key) { + if self.update_authority == UpdateAuthority::Address(*common.authority_info.key) { approve!() } else { abstain!() @@ -345,11 +376,13 @@ impl AssetV1 { } /// Validate the update external plugin adapter lifecycle event. - pub fn validate_update_external_plugin_adapter( + pub(crate) fn validate_update_external_plugin_adapter( &self, _authority_info: &AccountInfo, _: Option<&Plugin>, - _plugin: Option<&ExternalPluginAdapter>, + // _plugin: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } diff --git a/programs/mpl-core/src/state/collection.rs b/programs/mpl-core/src/state/collection.rs index f59fd388..4c68d8b9 100644 --- a/programs/mpl-core/src/state/collection.rs +++ b/programs/mpl-core/src/state/collection.rs @@ -1,10 +1,13 @@ use borsh::{BorshDeserialize, BorshSerialize}; use shank::ShankAccount; -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{ error::MplCoreError, - plugins::{abstain, approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{ + abstain, approve, AssetValidationCommon, AssetValidationContext, CheckResult, + ValidationResult, + }, }; use super::{Authority, CoreAsset, DataBlob, Key, SolanaAccount, UpdateAuthority}; @@ -119,13 +122,15 @@ impl CollectionV1 { } /// Validate the create lifecycle event. - pub fn validate_create( + pub(crate) fn validate_create( &self, - authority_info: &AccountInfo, - _new_plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _new_plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.update_authority { + if common.authority_info.key == &self.update_authority { approve!() } else { abstain!() @@ -133,127 +138,139 @@ impl CollectionV1 { } /// Validate the add plugin lifecycle event. - pub fn validate_add_plugin( + pub(crate) fn validate_add_plugin( &self, - authority_info: &AccountInfo, - new_plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // new_plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let new_plugin = match new_plugin { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - if *authority_info.key == self.update_authority - && new_plugin.manager() == Authority::UpdateAuthority - { - approve!() + if let AssetValidationContext::AddPlugin { new_plugin } = ctx { + if *common.authority_info.key == self.update_authority + && new_plugin.manager() == Authority::UpdateAuthority + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the remove plugin lifecycle event. - pub fn validate_remove_plugin( + pub(crate) fn validate_remove_plugin( &self, - authority_info: &AccountInfo, - plugin_to_remove: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin_to_remove: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let plugin_to_remove = match plugin_to_remove { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - if *authority_info.key == self.update_authority - && plugin_to_remove.manager() == Authority::UpdateAuthority - { - approve!() + if let AssetValidationContext::RemovePlugin { plugin_to_remove } = ctx { + if *common.authority_info.key == self.update_authority + && plugin_to_remove.manager() == Authority::UpdateAuthority + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the update plugin lifecycle event. - pub fn validate_update_plugin( + pub(crate) fn validate_update_plugin( &self, - _authority_info: &AccountInfo, - _plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } /// Validate the approve plugin authority lifecycle event. - pub fn validate_approve_plugin_authority( + pub(crate) fn validate_approve_plugin_authority( &self, - authority_info: &AccountInfo, - plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let plugin = match plugin { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - if *authority_info.key == self.update_authority - && plugin.manager() == Authority::UpdateAuthority - { - approve!() + if let AssetValidationContext::ApprovePluginAuthority { plugin } = ctx { + if *common.authority_info.key == self.update_authority + && plugin.manager() == Authority::UpdateAuthority + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the revoke plugin authority lifecycle event. - pub fn validate_revoke_plugin_authority( + pub(crate) fn validate_revoke_plugin_authority( &self, - authority_info: &AccountInfo, - plugin: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // plugin: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result { - let plugin = match plugin { - Some(plugin) => plugin, - None => return Err(MplCoreError::InvalidPlugin.into()), - }; - - if *authority_info.key == self.update_authority - && plugin.manager() == Authority::UpdateAuthority - { - approve!() + if let AssetValidationContext::RevokePluginAuthority { plugin } = ctx { + if *common.authority_info.key == self.update_authority + && plugin.manager() == Authority::UpdateAuthority + { + approve!() + } else { + abstain!() + } } else { - abstain!() + Err(MplCoreError::InvalidPlugin.into()) } } /// Validate the transfer lifecycle event. - pub fn validate_transfer( + pub(crate) fn validate_transfer( &self, - _authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } /// Validate the burn lifecycle event. - pub fn validate_burn( + pub(crate) fn validate_burn( &self, - _: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } /// Validate the update lifecycle event. - pub fn validate_update( + pub(crate) fn validate_update( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { - if authority_info.key == &self.update_authority { + if common.authority_info.key == &self.update_authority { approve!() } else { abstain!() @@ -261,34 +278,40 @@ impl CollectionV1 { } /// Validate the compress lifecycle event. - pub fn validate_compress( + pub(crate) fn validate_compress( &self, - _authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } /// Validate the decompress lifecycle event. - pub fn validate_decompress( + pub(crate) fn validate_decompress( &self, - _authority_info: &AccountInfo, - _: Option<&Plugin>, - _: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } /// Validate the add external plugin adapter lifecycle event. - pub fn validate_add_external_plugin_adapter( + pub(crate) fn validate_add_external_plugin_adapter( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _new_plugin: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _new_plugin: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { // Approve if the update authority matches the authority. - if *authority_info.key == self.update_authority { + if *common.authority_info.key == self.update_authority { approve!() } else { abstain!() @@ -296,13 +319,15 @@ impl CollectionV1 { } /// Validate the remove external plugin adapter lifecycle event. - pub fn validate_remove_external_plugin_adapter( + pub(crate) fn validate_remove_external_plugin_adapter( &self, - authority_info: &AccountInfo, - _: Option<&Plugin>, - _plugin_to_remove: Option<&ExternalPluginAdapter>, + // authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _plugin_to_remove: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { - if self.update_authority == *authority_info.key { + if common.authority_info.key == &self.update_authority { approve!() } else { abstain!() @@ -310,11 +335,13 @@ impl CollectionV1 { } /// Validate the update external plugin adapter lifecycle event. - pub fn validate_update_external_plugin_adapter( + pub(crate) fn validate_update_external_plugin_adapter( &self, - _authority_info: &AccountInfo, - _: Option<&Plugin>, - _plugin: Option<&ExternalPluginAdapter>, + // _authority_info: &AccountInfo, + // _: Option<&Plugin>, + // _plugin: Option<&ExternalPluginAdapter>, + _common: &AssetValidationCommon, + _ctx: &AssetValidationContext, ) -> Result { abstain!() } diff --git a/programs/mpl-core/src/utils/mod.rs b/programs/mpl-core/src/utils/mod.rs index 96fbf998..d2e47cea 100644 --- a/programs/mpl-core/src/utils/mod.rs +++ b/programs/mpl-core/src/utils/mod.rs @@ -7,10 +7,11 @@ pub(crate) use compression::*; use crate::{ error::MplCoreError, plugins::{ - validate_external_plugin_adapter_checks, validate_plugin_checks, CheckResult, - ExternalCheckResultBits, ExternalPluginAdapter, ExternalPluginAdapterKey, - ExternalRegistryRecord, HookableLifecycleEvent, Plugin, PluginHeaderV1, PluginRegistryV1, - PluginType, PluginValidationContext, RegistryRecord, ValidationResult, + validate_external_plugin_adapter_checks, validate_plugin_checks, AssetValidationCommon, + AssetValidationContext, CheckResult, ExternalCheckResultBits, ExternalPluginAdapter, + ExternalPluginAdapterKey, ExternalRegistryRecord, HookableLifecycleEvent, Plugin, + PluginHeaderV1, PluginRegistryV1, PluginType, PluginValidationContext, RegistryRecord, + ValidationResult, }, state::{ AssetV1, Authority, CollectionV1, CoreAsset, DataBlob, Key, SolanaAccount, UpdateAuthority, @@ -20,7 +21,6 @@ use mpl_utils::assert_signer; use num_traits::FromPrimitive; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - pubkey::Pubkey, }; use std::collections::BTreeMap; @@ -102,38 +102,48 @@ pub fn fetch_core_data( #[allow(clippy::too_many_arguments, clippy::type_complexity)] /// Validate asset permissions using lifecycle validations for asset, collection, and plugins. -pub(crate) fn validate_asset_permissions<'a>( - accounts: &'a [AccountInfo<'a>], - authority_info: &'a AccountInfo<'a>, - asset: &'a AccountInfo<'a>, - collection: Option<&'a AccountInfo<'a>>, - new_owner: Option<&'a AccountInfo<'a>>, - new_authority: Option<&UpdateAuthority>, - new_plugin: Option<&Plugin>, - new_external_plugin_adapter: Option<&ExternalPluginAdapter>, +pub(crate) fn validate_asset_permissions<'a, 'b>( + // accounts: &'a [AccountInfo<'a>], + // authority_info: &'a AccountInfo<'a>, + // asset: &'a AccountInfo<'a>, + // collection: Option<&'a AccountInfo<'a>>, + // new_owner: Option<&'a AccountInfo<'a>>, + // new_authority: Option<&UpdateAuthority>, + // new_plugin: Option<&Plugin>, + // new_external_plugin_adapter: Option<&ExternalPluginAdapter>, + common: &'b AssetValidationCommon<'a>, + ctx: &'b AssetValidationContext<'a>, asset_check_fp: fn() -> CheckResult, collection_check_fp: fn() -> CheckResult, plugin_check_fp: fn(&PluginType) -> CheckResult, asset_validate_fp: fn( &AssetV1, - &AccountInfo, - Option<&Plugin>, - Option<&ExternalPluginAdapter>, + // &AccountInfo, + // Option<&Plugin>, + // Option<&ExternalPluginAdapter>, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, collection_validate_fp: fn( &CollectionV1, - &AccountInfo, - Option<&Plugin>, - Option<&ExternalPluginAdapter>, + // &AccountInfo, + // Option<&Plugin>, + // Option<&ExternalPluginAdapter>, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, plugin_validate_fp: fn( &Plugin, &PluginValidationContext, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, external_plugin_adapter_validate_fp: Option< fn( &ExternalPluginAdapter, &PluginValidationContext, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, >, hookable_lifecycle_event: Option, @@ -144,18 +154,22 @@ pub(crate) fn validate_asset_permissions<'a>( panic!("Missing function parameters to validate_asset_permissions"); } - let (deserialized_asset, plugin_header, plugin_registry) = fetch_core_data::(asset)?; - let resolved_authorities = - resolve_pubkey_to_authorities(authority_info, collection, &deserialized_asset)?; + let (deserialized_asset, plugin_header, plugin_registry) = + fetch_core_data::(common.asset_info)?; + let resolved_authorities = resolve_pubkey_to_authorities( + common.authority_info, + common.collection_info, + &deserialized_asset, + )?; // If the asset is part of a collection, the collection must be passed in and it must be correct. if let UpdateAuthority::Collection(collection_address) = deserialized_asset.update_authority { - if collection.is_none() { + if common.collection_info.is_none() { return Err(MplCoreError::MissingCollection.into()); - } else if collection.unwrap().key != &collection_address { + } else if common.collection_info.unwrap().key != &collection_address { return Err(MplCoreError::InvalidCollection.into()); } - } else if collection.is_some() { + } else if common.collection_info.is_some() { return Err(MplCoreError::InvalidCollection.into()); } @@ -167,14 +181,14 @@ pub(crate) fn validate_asset_permissions<'a>( // The asset approval overrides the collection approval. let asset_check = asset_check_fp(); - let collection_check = if collection.is_some() { + let collection_check = if common.collection_info.is_some() { collection_check_fp() } else { CheckResult::None }; // Check the collection plugins first. - if let Some(collection_info) = collection { + if let Some(collection_info) = common.collection_info { let (_, _, registry) = fetch_core_data::(collection_info)?; if let Some(r) = registry { @@ -197,7 +211,7 @@ pub(crate) fn validate_asset_permissions<'a>( registry.check_registry(Key::AssetV1, plugin_check_fp, &mut checks); if let Some(lifecycle_event) = &hookable_lifecycle_event { registry.check_adapter_registry( - asset, + common.asset_info, Key::AssetV1, lifecycle_event, &mut external_checks, @@ -211,9 +225,11 @@ pub(crate) fn validate_asset_permissions<'a>( if asset_check != CheckResult::None { match asset_validate_fp( &deserialized_asset, - authority_info, - new_plugin, - new_external_plugin_adapter, + // common.authority_info, + // new_plugin, + // new_external_plugin_adapter, + common, + ctx, )? { ValidationResult::Approved => approved = true, ValidationResult::Rejected => rejected = true, @@ -226,10 +242,12 @@ pub(crate) fn validate_asset_permissions<'a>( if collection_check != CheckResult::None { match collection_validate_fp( - &CollectionV1::load(collection.ok_or(MplCoreError::MissingCollection)?, 0)?, - authority_info, - new_plugin, - new_external_plugin_adapter, + &CollectionV1::load(common.collection_info.unwrap(), 0)?, + // common.authority_info, + // new_plugin, + // new_external_plugin_adapter, + common, + ctx, )? { ValidationResult::Approved => approved = true, ValidationResult::Rejected => rejected = true, @@ -242,15 +260,18 @@ pub(crate) fn validate_asset_permissions<'a>( match validate_plugin_checks( Key::CollectionV1, - accounts, + // common.accounts, &checks, - authority_info, - new_owner, - new_authority, - None, - new_plugin, - Some(asset), - collection, + // common.authority_info, + // new_owner, + // new_authority, + // None, + // new_plugin, + // Some(common.asset_info), + // common.collection_info, + // Some(common.asset_info), + common, + ctx, &resolved_authorities, plugin_validate_fp, )? { @@ -264,15 +285,18 @@ pub(crate) fn validate_asset_permissions<'a>( match validate_plugin_checks( Key::AssetV1, - accounts, + // common.accounts, &checks, - authority_info, - new_owner, - new_authority, - None, - new_plugin, - Some(asset), - collection, + // common.authority_info, + // new_owner, + // new_authority, + // None, + // new_plugin, + // Some(common.asset_info), + // common.collection_info, + // Some(common.asset_info), + common, + ctx, &resolved_authorities, plugin_validate_fp, )? { @@ -287,15 +311,17 @@ pub(crate) fn validate_asset_permissions<'a>( if let Some(external_plugin_adapter_validate_fp) = external_plugin_adapter_validate_fp { match validate_external_plugin_adapter_checks( Key::CollectionV1, - accounts, + // common.accounts, &external_checks, - authority_info, - new_owner, - new_authority, - None, - new_plugin, - Some(asset), - collection, + // common.authority_info, + // new_owner, + // new_authority, + // None, + // new_plugin, + // Some(common.asset_info), + // common.collection_info, + common, + ctx, &resolved_authorities, external_plugin_adapter_validate_fp, )? { @@ -308,15 +334,17 @@ pub(crate) fn validate_asset_permissions<'a>( match validate_external_plugin_adapter_checks( Key::AssetV1, - accounts, + // common.accounts, &external_checks, - authority_info, - new_owner, - new_authority, - None, - new_plugin, - Some(asset), - collection, + // common.authority_info, + // new_owner, + // new_authority, + // None, + // new_plugin, + // Some(common.asset_info), + // common.collection_info, + common, + ctx, &resolved_authorities, external_plugin_adapter_validate_fp, )? { @@ -340,28 +368,36 @@ pub(crate) fn validate_asset_permissions<'a>( /// Validate collection permissions using lifecycle validations for collection and plugins. #[allow(clippy::type_complexity, clippy::too_many_arguments)] pub(crate) fn validate_collection_permissions<'a>( - accounts: &'a [AccountInfo<'a>], - authority_info: &'a AccountInfo<'a>, - collection: &'a AccountInfo<'a>, - new_authority: Option<&Pubkey>, - new_plugin: Option<&Plugin>, - new_external_plugin_adapter: Option<&ExternalPluginAdapter>, + // accounts: &'a [AccountInfo<'a>], + // authority_info: &'a AccountInfo<'a>, + // collection: &'a AccountInfo<'a>, + // new_authority: Option<&Pubkey>, + // new_plugin: Option<&Plugin>, + // new_external_plugin_adapter: Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon<'a>, + ctx: &AssetValidationContext<'a>, collection_check_fp: fn() -> CheckResult, plugin_check_fp: fn(&PluginType) -> CheckResult, collection_validate_fp: fn( &CollectionV1, - &AccountInfo, - Option<&Plugin>, - Option<&ExternalPluginAdapter>, + // &AccountInfo, + // Option<&Plugin>, + // Option<&ExternalPluginAdapter>, + common: &AssetValidationCommon, + ctx: &AssetValidationContext, ) -> Result, plugin_validate_fp: fn( &Plugin, &PluginValidationContext, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, external_plugin_adapter_validate_fp: Option< fn( &ExternalPluginAdapter, &PluginValidationContext, + &AssetValidationCommon, + &AssetValidationContext, ) -> Result, >, hookable_lifecycle_event: Option, @@ -380,9 +416,9 @@ pub(crate) fn validate_collection_permissions<'a>( } let (deserialized_collection, plugin_header, plugin_registry) = - fetch_core_data::(collection)?; + fetch_core_data::(common.asset_info)?; let resolved_authorities = - resolve_pubkey_to_authorities_collection(authority_info, collection)?; + resolve_pubkey_to_authorities_collection(common.authority_info, common.asset_info)?; let mut checks: BTreeMap = BTreeMap::new(); let mut external_checks: BTreeMap< ExternalPluginAdapterKey, @@ -396,7 +432,7 @@ pub(crate) fn validate_collection_permissions<'a>( registry.check_registry(Key::CollectionV1, plugin_check_fp, &mut checks); if let Some(lifecycle_event) = hookable_lifecycle_event { registry.check_adapter_registry( - collection, + common.asset_info, Key::CollectionV1, &lifecycle_event, &mut external_checks, @@ -417,9 +453,11 @@ pub(crate) fn validate_collection_permissions<'a>( let result = match core_check.0 { Key::CollectionV1 => collection_validate_fp( &deserialized_collection, - authority_info, - new_plugin, - new_external_plugin_adapter, + // common.authority_info, + // ctx.new_plugin, + // ctx.new_external_plugin_adapter, + common, + ctx, )?, _ => return Err(MplCoreError::IncorrectAccount.into()), }; @@ -435,15 +473,18 @@ pub(crate) fn validate_collection_permissions<'a>( match validate_plugin_checks( Key::CollectionV1, - accounts, + // accounts, &checks, - authority_info, - None, - None, - new_authority, - new_plugin, - None, - Some(collection), + // authority_info, + // None, + // None, + // new_authority, + // new_plugin, + // None, + // Some(collection), + // Some(common.asset_info), + common, + ctx, &resolved_authorities, plugin_validate_fp, )? { @@ -458,15 +499,17 @@ pub(crate) fn validate_collection_permissions<'a>( if let Some(external_plugin_adapter_validate_fp) = external_plugin_adapter_validate_fp { match validate_external_plugin_adapter_checks( Key::CollectionV1, - accounts, + // accounts, &external_checks, - authority_info, - None, - None, - new_authority, - new_plugin, - None, - Some(collection), + // authority_info, + // None, + // None, + // new_authority, + // new_plugin, + // None, + // Some(collection), + common, + ctx, &resolved_authorities, external_plugin_adapter_validate_fp, )? { diff --git a/programs/mpl-core/src/utils/plugin_system.rs b/programs/mpl-core/src/utils/plugin_system.rs new file mode 100644 index 00000000..b9b05b84 --- /dev/null +++ b/programs/mpl-core/src/utils/plugin_system.rs @@ -0,0 +1,140 @@ +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_memory::sol_memmove, +}; + +use crate::{ + error::MplCoreError, + plugins::PluginRegistryV1, + state::{CoreAsset, DataBlob, SolanaAccount}, +}; + +use super::resize_or_reallocate_account; + +pub(crate) struct PluginSystem<'a> { + account: &'a AccountInfo<'a>, + root: Option<&'a dyn CoreAsset>, + registry_offset: Option, + plugin_registry: Option<&'a PluginRegistryV1>, +} + +impl<'a> PluginSystem<'a> { + /// Create a new dynamic account info. + pub(crate) fn new(account_info: &'a AccountInfo<'a>) -> Self { + Self { + account: account_info, + root: None, + registry_offset: None, + plugin_registry: None, + } + } + + /// Insert data into the account at the given offset. + pub(crate) fn insert( + &self, + offset: usize, + data: &(impl DataBlob + SolanaAccount), + funder: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, + ) -> ProgramResult { + // Realloc the account to the new size. + let new_size = offset + data.get_size(); + resize_or_reallocate_account(self.account, funder, system_program, new_size)?; + // Insert the data. + data.save(self.account, offset)?; + + Ok(()) + } + + pub(crate) fn append( + &self, + data: impl DataBlob + SolanaAccount, + funder: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, + ) -> ProgramResult { + // Realloc the account to the new size. + let current_size = self.account.data_len(); + let new_size = current_size + data.get_size(); + resize_or_reallocate_account(self.account, funder, system_program, new_size)?; + // Insert the data. + data.save(self.account, current_size)?; + + Ok(()) + } + + pub(crate) fn replace( + &self, + offset: usize, + len: usize, + data: impl DataBlob + SolanaAccount, + funder: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, + ) -> ProgramResult { + // Check if the data is larger or smaller than the current data. + let data_len = data.get_size(); + let post_offset = offset + .checked_add(len) + .ok_or(MplCoreError::NumericalOverflow)?; + let new_post_offset = offset + .checked_add(data_len) + .ok_or(MplCoreError::NumericalOverflow)?; + let data_to_move_len = self.account.data_len().saturating_sub(post_offset); + #[allow(clippy::comparison_chain)] + if len > data_len { + // Move the data after the data being replaced. + unsafe { + let base = self.account.data.borrow_mut().as_mut_ptr(); + sol_memmove( + base.add(new_post_offset), + base.add(post_offset), + data_to_move_len, + ); + } + + // Realloc the account to the new size. + let new_size = offset + data_len; + resize_or_reallocate_account(self.account, funder, system_program, new_size)?; + } else if len < data_len { + // Realloc the account to the new size. + let new_size = offset + data_len; + resize_or_reallocate_account(self.account, funder, system_program, new_size)?; + + // Move the data after the data being replaced. + unsafe { + let base = self.account.data.borrow_mut().as_mut_ptr(); + sol_memmove( + base.add(new_post_offset), + base.add(post_offset), + data_to_move_len, + ); + } + } + + // Save the data. + data.save(self.account, offset)?; + + Ok(()) + } + + pub(crate) fn remove( + &self, + offset: usize, + len: usize, + funder: &AccountInfo<'a>, + system_program: &AccountInfo<'a>, + ) -> ProgramResult { + let post_offset = offset.checked_add(len).unwrap(); + let data_to_move_len = self.account.data_len().saturating_sub(post_offset); + + // Move the data. + unsafe { + let base = self.account.data.borrow_mut().as_mut_ptr(); + sol_memmove(base.add(offset), base.add(post_offset), data_to_move_len); + } + + // Realloc the account to the new size. + let new_size = self.account.data_len().saturating_sub(len); + resize_or_reallocate_account(self.account, funder, system_program, new_size)?; + + Ok(()) + } +}