From e939cb20e0b86e36be5edaeab2bb19e800a8e6a6 Mon Sep 17 00:00:00 2001 From: Michael Danenberg <56533526+danenbm@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:04:50 -0700 Subject: [PATCH] Add external plugin validation functions (#85) * Add validations for external plugins on lifecycle events * Remove external_plugin_validate from plugin_checks --- programs/mpl-core/src/plugins/data_store.rs | 25 +++++--- .../mpl-core/src/plugins/external_plugins.rs | 62 ++++++++++++++----- programs/mpl-core/src/plugins/lifecycle.rs | 7 ++- .../mpl-core/src/plugins/lifecycle_hook.rs | 25 +++++--- programs/mpl-core/src/plugins/oracle.rs | 25 +++++--- .../src/processor/add_external_plugin.rs | 14 +++-- programs/mpl-core/src/processor/add_plugin.rs | 2 + .../src/processor/approve_plugin_authority.rs | 2 + programs/mpl-core/src/processor/burn.rs | 4 +- programs/mpl-core/src/processor/compress.rs | 1 + programs/mpl-core/src/processor/create.rs | 6 +- .../src/processor/create_collection.rs | 6 +- programs/mpl-core/src/processor/decompress.rs | 1 + .../mpl-core/src/processor/remove_plugin.rs | 2 + .../src/processor/revoke_plugin_authority.rs | 2 + programs/mpl-core/src/processor/transfer.rs | 3 +- programs/mpl-core/src/processor/update.rs | 6 +- .../mpl-core/src/processor/update_plugin.rs | 2 + programs/mpl-core/src/utils.rs | 10 ++- 19 files changed, 148 insertions(+), 57 deletions(-) diff --git a/programs/mpl-core/src/plugins/data_store.rs b/programs/mpl-core/src/plugins/data_store.rs index 4e3163bb..91fdf54f 100644 --- a/programs/mpl-core/src/plugins/data_store.rs +++ b/programs/mpl-core/src/plugins/data_store.rs @@ -23,6 +23,22 @@ pub struct DataStore { pub data_len: usize, } +impl PluginValidation for DataStore { + fn validate_add_external_plugin( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } + + fn validate_transfer( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } +} + impl From<&DataStoreInitInfo> for DataStore { fn from(init_info: &DataStoreInitInfo) -> Self { Self { @@ -46,15 +62,6 @@ pub struct DataStoreInitInfo { pub schema: Option, } -impl PluginValidation for DataStoreInitInfo { - fn validate_add_external_plugin( - &self, - _ctx: &PluginValidationContext, - ) -> Result { - Ok(ValidationResult::Pass) - } -} - /// Data store update info. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Eq, PartialEq)] pub struct DataStoreUpdateInfo { diff --git a/programs/mpl-core/src/plugins/external_plugins.rs b/programs/mpl-core/src/plugins/external_plugins.rs index a0270220..8e684f9b 100644 --- a/programs/mpl-core/src/plugins/external_plugins.rs +++ b/programs/mpl-core/src/plugins/external_plugins.rs @@ -93,31 +93,63 @@ impl ExternalPlugin { /// Validate the add external plugin lifecycle event. pub(crate) fn validate_create( - init_info: &ExternalPluginInitInfo, + external_plugin: &ExternalPlugin, ctx: &PluginValidationContext, ) -> Result { - match init_info { - ExternalPluginInitInfo::LifecycleHook(init_info) => init_info.validate_create(ctx), - ExternalPluginInitInfo::Oracle(init_info) => init_info.validate_create(ctx), - ExternalPluginInitInfo::DataStore(init_info) => init_info.validate_create(ctx), + match external_plugin { + ExternalPlugin::LifecycleHook(lifecycle_hook) => lifecycle_hook.validate_create(ctx), + ExternalPlugin::Oracle(oracle) => oracle.validate_create(ctx), + ExternalPlugin::DataStore(data_store) => data_store.validate_create(ctx), + } + } + + /// Route the validation of the update action to the appropriate plugin. + pub(crate) fn validate_update( + external_plugin: &ExternalPlugin, + ctx: &PluginValidationContext, + ) -> Result { + match external_plugin { + ExternalPlugin::LifecycleHook(lifecycle_hook) => lifecycle_hook.validate_update(ctx), + ExternalPlugin::Oracle(oracle) => oracle.validate_update(ctx), + ExternalPlugin::DataStore(data_store) => data_store.validate_update(ctx), + } + } + + /// Route the validation of the burn action to the appropriate plugin. + pub(crate) fn validate_burn( + external_plugin: &ExternalPlugin, + ctx: &PluginValidationContext, + ) -> Result { + match external_plugin { + ExternalPlugin::LifecycleHook(lifecycle_hook) => lifecycle_hook.validate_burn(ctx), + ExternalPlugin::Oracle(oracle) => oracle.validate_burn(ctx), + ExternalPlugin::DataStore(data_store) => data_store.validate_burn(ctx), + } + } + + /// Route the validation of the transfer action to the appropriate external plugin. + pub(crate) fn validate_transfer( + external_plugin: &ExternalPlugin, + ctx: &PluginValidationContext, + ) -> Result { + match external_plugin { + ExternalPlugin::LifecycleHook(lifecycle_hook) => lifecycle_hook.validate_transfer(ctx), + ExternalPlugin::Oracle(oracle) => oracle.validate_transfer(ctx), + ExternalPlugin::DataStore(data_store) => data_store.validate_transfer(ctx), } } /// Validate the add external plugin lifecycle event. pub(crate) fn validate_add_external_plugin( - init_info: &ExternalPluginInitInfo, + external_plugin: &ExternalPlugin, ctx: &PluginValidationContext, ) -> Result { - match init_info { - ExternalPluginInitInfo::LifecycleHook(init_info) => { - init_info.validate_add_external_plugin(ctx) - } - ExternalPluginInitInfo::Oracle(init_info) => { - init_info.validate_add_external_plugin(ctx) - } - ExternalPluginInitInfo::DataStore(init_info) => { - init_info.validate_add_external_plugin(ctx) + match external_plugin { + ExternalPlugin::LifecycleHook(lifecycle_hook) => { + lifecycle_hook.validate_add_external_plugin(ctx) } + ExternalPlugin::Oracle(oracle) => oracle.validate_add_external_plugin(ctx), + ExternalPlugin::DataStore(data_store) => data_store.validate_add_external_plugin(ctx), } } diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 67d8bbdb..28b2d34b 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -811,7 +811,10 @@ pub(crate) fn validate_plugin_checks<'a>( asset: Option<&AccountInfo<'a>>, collection: Option<&AccountInfo<'a>>, resolved_authorities: &[Authority], - validate_fp: fn(&Plugin, &PluginValidationContext) -> Result, + plugin_validate_fp: fn( + &Plugin, + &PluginValidationContext, + ) -> Result, ) -> Result { let mut approved = false; let mut rejected = false; @@ -836,7 +839,7 @@ pub(crate) fn validate_plugin_checks<'a>( target_plugin: new_plugin, }; - let result = validate_fp(&Plugin::load(account, registry_record.offset)?, &ctx)?; + let result = plugin_validate_fp(&Plugin::load(account, registry_record.offset)?, &ctx)?; match result { ValidationResult::Rejected => rejected = true, ValidationResult::Approved => approved = true, diff --git a/programs/mpl-core/src/plugins/lifecycle_hook.rs b/programs/mpl-core/src/plugins/lifecycle_hook.rs index 616c0bdc..870643df 100644 --- a/programs/mpl-core/src/plugins/lifecycle_hook.rs +++ b/programs/mpl-core/src/plugins/lifecycle_hook.rs @@ -30,6 +30,22 @@ pub struct LifecycleHook { pub data_len: usize, // 8 } +impl PluginValidation for LifecycleHook { + fn validate_add_external_plugin( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } + + fn validate_transfer( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } +} + impl From<&LifecycleHookInitInfo> for LifecycleHook { fn from(init_info: &LifecycleHookInitInfo) -> Self { Self { @@ -62,15 +78,6 @@ pub struct LifecycleHookInitInfo { pub schema: Option, } -impl PluginValidation for LifecycleHookInitInfo { - fn validate_add_external_plugin( - &self, - _ctx: &PluginValidationContext, - ) -> Result { - Ok(ValidationResult::Pass) - } -} - /// Lifecycle hook update info. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Eq, PartialEq)] pub struct LifecycleHookUpdateInfo { diff --git a/programs/mpl-core/src/plugins/oracle.rs b/programs/mpl-core/src/plugins/oracle.rs index 4896ee86..f4f61d7f 100644 --- a/programs/mpl-core/src/plugins/oracle.rs +++ b/programs/mpl-core/src/plugins/oracle.rs @@ -20,6 +20,22 @@ pub struct Oracle { pub results_offset: ValidationResultsOffset, } +impl PluginValidation for Oracle { + fn validate_add_external_plugin( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } + + fn validate_transfer( + &self, + _ctx: &PluginValidationContext, + ) -> Result { + Ok(ValidationResult::Pass) + } +} + impl From<&OracleInitInfo> for Oracle { fn from(init_info: &OracleInitInfo) -> Self { Self { @@ -50,15 +66,6 @@ pub struct OracleInitInfo { pub results_offset: Option, } -impl PluginValidation for OracleInitInfo { - fn validate_add_external_plugin( - &self, - _ctx: &PluginValidationContext, - ) -> Result { - Ok(ValidationResult::Pass) - } -} - /// Oracle update info. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Eq, PartialEq)] pub struct OracleUpdateInfo { diff --git a/programs/mpl-core/src/processor/add_external_plugin.rs b/programs/mpl-core/src/processor/add_external_plugin.rs index 8ff938e7..292cf8f1 100644 --- a/programs/mpl-core/src/processor/add_external_plugin.rs +++ b/programs/mpl-core/src/processor/add_external_plugin.rs @@ -55,8 +55,10 @@ pub(crate) fn add_external_plugin<'a>( target_plugin: None, }; - if ExternalPlugin::validate_add_external_plugin(&args.init_info, &validation_ctx)? - == ValidationResult::Rejected + if ExternalPlugin::validate_add_external_plugin( + &ExternalPlugin::from(&args.init_info), + &validation_ctx, + )? == ValidationResult::Rejected { return Err(MplCoreError::InvalidAuthority.into()); } @@ -75,6 +77,7 @@ pub(crate) fn add_external_plugin<'a>( AssetV1::validate_add_external_plugin, CollectionV1::validate_add_external_plugin, Plugin::validate_add_external_plugin, + Some(ExternalPlugin::validate_add_external_plugin), )?; // Increment sequence number and save only if it is `Some(_)`. @@ -123,8 +126,10 @@ pub(crate) fn add_collection_external_plugin<'a>( target_plugin: None, }; - if ExternalPlugin::validate_add_external_plugin(&args.init_info, &validation_ctx)? - == ValidationResult::Rejected + if ExternalPlugin::validate_add_external_plugin( + &ExternalPlugin::from(&args.init_info), + &validation_ctx, + )? == ValidationResult::Rejected { return Err(MplCoreError::InvalidAuthority.into()); } @@ -139,6 +144,7 @@ pub(crate) fn add_collection_external_plugin<'a>( PluginType::check_add_external_plugin, CollectionV1::validate_add_external_plugin, Plugin::validate_add_external_plugin, + Some(ExternalPlugin::validate_add_external_plugin), )?; process_add_external_plugin::( diff --git a/programs/mpl-core/src/processor/add_plugin.rs b/programs/mpl-core/src/processor/add_plugin.rs index 4f4581bd..c05bbda6 100644 --- a/programs/mpl-core/src/processor/add_plugin.rs +++ b/programs/mpl-core/src/processor/add_plugin.rs @@ -73,6 +73,7 @@ pub(crate) fn add_plugin<'a>( AssetV1::validate_add_plugin, CollectionV1::validate_add_plugin, Plugin::validate_add_plugin, + None, )?; // Increment sequence number and save only if it is `Some(_)`. @@ -141,6 +142,7 @@ pub(crate) fn add_collection_plugin<'a>( PluginType::check_add_plugin, CollectionV1::validate_add_plugin, Plugin::validate_add_plugin, + None, )?; process_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 7e7d44fc..f1a82064 100644 --- a/programs/mpl-core/src/processor/approve_plugin_authority.rs +++ b/programs/mpl-core/src/processor/approve_plugin_authority.rs @@ -63,6 +63,7 @@ pub(crate) fn approve_plugin_authority<'a>( AssetV1::validate_approve_plugin_authority, CollectionV1::validate_approve_plugin_authority, Plugin::validate_approve_plugin_authority, + None, )?; // Increment sequence number and save only if it is `Some(_)`. @@ -117,6 +118,7 @@ pub(crate) fn approve_collection_plugin_authority<'a>( PluginType::check_approve_plugin_authority, CollectionV1::validate_approve_plugin_authority, Plugin::validate_approve_plugin_authority, + None, )?; process_approve_plugin_authority::( diff --git a/programs/mpl-core/src/processor/burn.rs b/programs/mpl-core/src/processor/burn.rs index 6987e23d..5159ae1b 100644 --- a/programs/mpl-core/src/processor/burn.rs +++ b/programs/mpl-core/src/processor/burn.rs @@ -5,7 +5,7 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::{BurnCollectionV1Accounts, BurnV1Accounts}, - plugins::{Plugin, PluginType}, + plugins::{ExternalPlugin, Plugin, PluginType}, state::{AssetV1, CollectionV1, CompressionProof, Key, SolanaAccount, Wrappable}, utils::{ close_program_account, load_key, rebuild_account_state_from_proof_data, resolve_authority, @@ -96,6 +96,7 @@ pub(crate) fn burn<'a>(accounts: &'a [AccountInfo<'a>], args: BurnV1Args) -> Pro AssetV1::validate_burn, CollectionV1::validate_burn, Plugin::validate_burn, + Some(ExternalPlugin::validate_burn), )?; process_burn(ctx.accounts.asset, authority)?; @@ -138,6 +139,7 @@ pub(crate) fn burn_collection<'a>( PluginType::check_burn, CollectionV1::validate_burn, Plugin::validate_burn, + Some(ExternalPlugin::validate_burn), )?; process_burn(ctx.accounts.collection, authority) diff --git a/programs/mpl-core/src/processor/compress.rs b/programs/mpl-core/src/processor/compress.rs index fbbbbf2f..e45cc3b1 100644 --- a/programs/mpl-core/src/processor/compress.rs +++ b/programs/mpl-core/src/processor/compress.rs @@ -56,6 +56,7 @@ pub(crate) fn compress<'a>( AssetV1::validate_compress, CollectionV1::validate_compress, Plugin::validate_compress, + None, )?; // Compress the asset and plugin registry into account space. diff --git a/programs/mpl-core/src/processor/create.rs b/programs/mpl-core/src/processor/create.rs index 0fe139c2..19c886ed 100644 --- a/programs/mpl-core/src/processor/create.rs +++ b/programs/mpl-core/src/processor/create.rs @@ -209,8 +209,10 @@ pub(crate) fn process_create<'a>( new_owner: None, target_plugin: None, }; - if ExternalPlugin::validate_create(plugin_init_info, &validation_ctx)? - == ValidationResult::Rejected + if ExternalPlugin::validate_create( + &ExternalPlugin::from(plugin_init_info), + &validation_ctx, + )? == 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 c16020c2..460bc7f8 100644 --- a/programs/mpl-core/src/processor/create_collection.rs +++ b/programs/mpl-core/src/processor/create_collection.rs @@ -174,8 +174,10 @@ pub(crate) fn process_create_collection<'a>( new_owner: None, target_plugin: None, }; - if ExternalPlugin::validate_create(plugin_init_info, &validation_ctx)? - == ValidationResult::Rejected + if ExternalPlugin::validate_create( + &ExternalPlugin::from(plugin_init_info), + &validation_ctx, + )? == ValidationResult::Rejected { approved = false; }; diff --git a/programs/mpl-core/src/processor/decompress.rs b/programs/mpl-core/src/processor/decompress.rs index 212e9c0b..ae032d3c 100644 --- a/programs/mpl-core/src/processor/decompress.rs +++ b/programs/mpl-core/src/processor/decompress.rs @@ -72,6 +72,7 @@ pub(crate) fn decompress<'a>( AssetV1::validate_decompress, CollectionV1::validate_decompress, Plugin::validate_decompress, + None, )?; // TODO Enable compression. diff --git a/programs/mpl-core/src/processor/remove_plugin.rs b/programs/mpl-core/src/processor/remove_plugin.rs index 051cb355..73d42bed 100644 --- a/programs/mpl-core/src/processor/remove_plugin.rs +++ b/programs/mpl-core/src/processor/remove_plugin.rs @@ -69,6 +69,7 @@ pub(crate) fn remove_plugin<'a>( AssetV1::validate_remove_plugin, CollectionV1::validate_remove_plugin, Plugin::validate_remove_plugin, + None, )?; // Increment sequence number and save only if it is `Some(_)`. @@ -133,6 +134,7 @@ pub(crate) fn remove_collection_plugin<'a>( PluginType::check_remove_plugin, CollectionV1::validate_remove_plugin, Plugin::validate_remove_plugin, + None, )?; process_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 3537fbfe..e94c9a67 100644 --- a/programs/mpl-core/src/processor/revoke_plugin_authority.rs +++ b/programs/mpl-core/src/processor/revoke_plugin_authority.rs @@ -70,6 +70,7 @@ pub(crate) fn revoke_plugin_authority<'a>( AssetV1::validate_revoke_plugin_authority, CollectionV1::validate_revoke_plugin_authority, Plugin::validate_revoke_plugin_authority, + None, )?; // Increment sequence number and save only if it is `Some(_)`. @@ -138,6 +139,7 @@ pub(crate) fn revoke_collection_plugin_authority<'a>( PluginType::check_revoke_plugin_authority, CollectionV1::validate_revoke_plugin_authority, Plugin::validate_revoke_plugin_authority, + None, )?; let resolved_authorities = diff --git a/programs/mpl-core/src/processor/transfer.rs b/programs/mpl-core/src/processor/transfer.rs index 197ad626..d3399c4a 100644 --- a/programs/mpl-core/src/processor/transfer.rs +++ b/programs/mpl-core/src/processor/transfer.rs @@ -5,7 +5,7 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ error::MplCoreError, instruction::accounts::TransferV1Accounts, - plugins::{Plugin, PluginType}, + plugins::{ExternalPlugin, Plugin, PluginType}, state::{AssetV1, Authority, CollectionV1, CompressionProof, Key, SolanaAccount, Wrappable}, utils::{ compress_into_account_space, load_key, rebuild_account_state_from_proof_data, @@ -89,6 +89,7 @@ pub(crate) fn transfer<'a>(accounts: &'a [AccountInfo<'a>], args: TransferV1Args AssetV1::validate_transfer, CollectionV1::validate_transfer, Plugin::validate_transfer, + Some(ExternalPlugin::validate_transfer), )?; // Reset every owner-managed plugin in the registry. diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index 315ec7b9..acac1236 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -7,7 +7,9 @@ use solana_program::{ use crate::{ error::MplCoreError, instruction::accounts::{UpdateCollectionV1Accounts, UpdateV1Accounts}, - plugins::{Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, RegistryRecord}, + plugins::{ + ExternalPlugin, Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, RegistryRecord, + }, state::{AssetV1, CollectionV1, DataBlob, Key, SolanaAccount, UpdateAuthority}, utils::{ load_key, resize_or_reallocate_account, resolve_authority, validate_asset_permissions, @@ -59,6 +61,7 @@ pub(crate) fn update<'a>(accounts: &'a [AccountInfo<'a>], args: UpdateV1Args) -> AssetV1::validate_update, CollectionV1::validate_update, Plugin::validate_update, + Some(ExternalPlugin::validate_update), )?; // Increment sequence number and save only if it is `Some(_)`. @@ -142,6 +145,7 @@ pub(crate) fn update_collection<'a>( PluginType::check_update, CollectionV1::validate_update, Plugin::validate_update, + Some(ExternalPlugin::validate_update), )?; let collection_size = collection.get_size() as isize; diff --git a/programs/mpl-core/src/processor/update_plugin.rs b/programs/mpl-core/src/processor/update_plugin.rs index 62100470..6e5fa353 100644 --- a/programs/mpl-core/src/processor/update_plugin.rs +++ b/programs/mpl-core/src/processor/update_plugin.rs @@ -60,6 +60,7 @@ pub(crate) fn update_plugin<'a>( AssetV1::validate_update_plugin, CollectionV1::validate_update_plugin, Plugin::validate_update_plugin, + None, )?; let mut plugin_registry = plugin_registry.ok_or(MplCoreError::PluginsNotInitialized)?; @@ -188,6 +189,7 @@ pub(crate) fn update_collection_plugin<'a>( PluginType::check_update_plugin, CollectionV1::validate_update_plugin, Plugin::validate_update_plugin, + None, )?; // let (collection, plugin_header, plugin_registry) = diff --git a/programs/mpl-core/src/utils.rs b/programs/mpl-core/src/utils.rs index d6aa09d0..65c47a97 100644 --- a/programs/mpl-core/src/utils.rs +++ b/programs/mpl-core/src/utils.rs @@ -13,8 +13,8 @@ use crate::{ error::MplCoreError, plugins::{ create_meta_idempotent, initialize_plugin, validate_plugin_checks, CheckResult, - ExternalPluginInitInfo, Plugin, PluginHeaderV1, PluginRegistryV1, PluginType, - PluginValidationContext, RegistryRecord, ValidationResult, + ExternalPlugin, ExternalPluginInitInfo, Plugin, PluginHeaderV1, PluginRegistryV1, + PluginType, PluginValidationContext, RegistryRecord, ValidationResult, }, state::{ AssetV1, Authority, CollectionV1, Compressible, CompressionProof, CoreAsset, DataBlob, @@ -220,6 +220,9 @@ pub(crate) fn validate_asset_permissions<'a>( &Plugin, &PluginValidationContext, ) -> Result, + _external_plugin_validate_fp: Option< + fn(&ExternalPlugin, &PluginValidationContext) -> Result, + >, ) -> Result<(AssetV1, Option, Option), ProgramError> { let (deserialized_asset, plugin_header, plugin_registry) = fetch_core_data::(asset)?; let resolved_authorities = @@ -363,6 +366,9 @@ pub(crate) fn validate_collection_permissions<'a>( &Plugin, &PluginValidationContext, ) -> Result, + _external_plugin_validate_fp: Option< + fn(&ExternalPlugin, &PluginValidationContext) -> Result, + >, ) -> Result< ( CollectionV1,