From 9865f4e45ebdbafd9826a374b6f8a04a6ef74d6f Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Wed, 5 Jun 2024 14:26:57 -0400 Subject: [PATCH 1/3] Adding macros for approve/reject/forceapprove --- programs/mpl-core/src/plugins/add_blocker.rs | 4 +- .../mpl-core/src/plugins/burn_delegate.rs | 8 +-- programs/mpl-core/src/plugins/edition.rs | 13 ++-- .../mpl-core/src/plugins/freeze_delegate.rs | 20 ++---- .../src/plugins/immutable_metadata.rs | 4 +- programs/mpl-core/src/plugins/lifecycle.rs | 67 +++++++++++++------ .../src/plugins/permanent_burn_delegate.rs | 14 ++-- .../src/plugins/permanent_freeze_delegate.rs | 19 ++---- .../plugins/permanent_transfer_delegate.rs | 14 ++-- programs/mpl-core/src/plugins/royalties.rs | 11 ++- programs/mpl-core/src/plugins/transfer.rs | 11 ++- .../mpl-core/src/plugins/update_delegate.rs | 14 ++-- programs/mpl-core/src/state/asset.rs | 38 ++++------- programs/mpl-core/src/state/collection.rs | 26 +++---- .../mpl-core/src/state/update_authority.rs | 15 +++-- 15 files changed, 130 insertions(+), 148 deletions(-) diff --git a/programs/mpl-core/src/plugins/add_blocker.rs b/programs/mpl-core/src/plugins/add_blocker.rs index 813923bd..4e70ae12 100644 --- a/programs/mpl-core/src/plugins/add_blocker.rs +++ b/programs/mpl-core/src/plugins/add_blocker.rs @@ -3,7 +3,7 @@ use solana_program::program_error::ProgramError; use crate::state::{Authority, DataBlob}; -use super::{PluginType, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult}; /// The AddBlocker plugin prevents any plugin except for owner-managed plugins from being added. /// The default authority for this plugin is None. @@ -35,6 +35,6 @@ impl PluginValidation for AddBlocker { } } - Ok(ValidationResult::Rejected) + reject!() } } diff --git a/programs/mpl-core/src/plugins/burn_delegate.rs b/programs/mpl-core/src/plugins/burn_delegate.rs index 67f3226e..c4326586 100644 --- a/programs/mpl-core/src/plugins/burn_delegate.rs +++ b/programs/mpl-core/src/plugins/burn_delegate.rs @@ -2,7 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::PluginType, + plugins::{approve, PluginType}, state::{Authority, DataBlob}, }; @@ -47,8 +47,7 @@ impl PluginValidation for BurnDelegate { address: *ctx.authority_info.key, }) { - solana_program::msg!("BurnDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -66,8 +65,7 @@ impl PluginValidation for BurnDelegate { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::BurnDelegate { - solana_program::msg!("BurnDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/edition.rs b/programs/mpl-core/src/plugins/edition.rs index ab82359a..d0d7e279 100644 --- a/programs/mpl-core/src/plugins/edition.rs +++ b/programs/mpl-core/src/plugins/edition.rs @@ -3,7 +3,9 @@ use solana_program::program_error::ProgramError; use crate::state::Authority; -use super::{PluginType, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + approve, reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult, +}; /// The edition plugin allows the creator to set an edition number on the asset /// The default authority for this plugin is the creator. @@ -24,8 +26,7 @@ impl PluginValidation for Edition { if ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Edition { - solana_program::msg!("Edition: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -40,8 +41,7 @@ impl PluginValidation for Edition { if ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Edition { - solana_program::msg!("Edition: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -58,8 +58,7 @@ impl PluginValidation for Edition { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Edition { - solana_program::msg!("Edition: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/freeze_delegate.rs b/programs/mpl-core/src/plugins/freeze_delegate.rs index ba6314f5..a40c7193 100644 --- a/programs/mpl-core/src/plugins/freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/freeze_delegate.rs @@ -3,7 +3,7 @@ use solana_program::program_error::ProgramError; use crate::state::{Authority, DataBlob}; -use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; /// The freeze delegate plugin allows any authority to lock the asset so it's no longer transferable. /// The default authority for this plugin is the owner. @@ -43,8 +43,7 @@ impl PluginValidation for FreezeDelegate { _ctx: &PluginValidationContext, ) -> Result { if self.frozen { - solana_program::msg!("FreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -55,8 +54,7 @@ impl PluginValidation for FreezeDelegate { _ctx: &PluginValidationContext, ) -> Result { if self.frozen { - solana_program::msg!("FreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -68,8 +66,7 @@ impl PluginValidation for FreezeDelegate { ) -> Result { if let Some(Plugin::FreezeDelegate(freeze)) = ctx.target_plugin { if freeze.frozen { - solana_program::msg!("FreezeDelegate: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } } Ok(ValidationResult::Pass) @@ -82,15 +79,13 @@ impl PluginValidation for FreezeDelegate { ) -> Result { if let Some(Plugin::FreezeDelegate(freeze)) = ctx.target_plugin { if freeze.frozen { - solana_program::msg!("FreezeDelegate: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } else if ctx.self_authority == &(Authority::Address { address: *ctx.authority_info.key, }) { - solana_program::msg!("FreezeDelegate: Approved"); - return Ok(ValidationResult::Approved); + return approve!(); } } @@ -103,8 +98,7 @@ impl PluginValidation for FreezeDelegate { ctx: &PluginValidationContext, ) -> Result { if ctx.target_plugin.is_some() && self.frozen { - solana_program::msg!("FreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/immutable_metadata.rs b/programs/mpl-core/src/plugins/immutable_metadata.rs index fb9d1107..2096445c 100644 --- a/programs/mpl-core/src/plugins/immutable_metadata.rs +++ b/programs/mpl-core/src/plugins/immutable_metadata.rs @@ -3,7 +3,7 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; -use super::{PluginValidation, PluginValidationContext, ValidationResult}; +use super::{reject, PluginValidation, PluginValidationContext, ValidationResult}; /// The immutable metadata plugin allows its authority to prevent plugin's meta from changing. /// The default authority for this plugin is None. @@ -28,6 +28,6 @@ impl PluginValidation for ImmutableMetadata { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Rejected) + reject!() } } diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 8871f1e1..2a3162e9 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -249,8 +249,7 @@ impl Plugin { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::from(plugin) { - solana_program::msg!("Base: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } match plugin { @@ -332,8 +331,7 @@ impl Plugin { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::from(plugin) { - solana_program::msg!("Base: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } match plugin { @@ -463,21 +461,21 @@ impl Plugin { match (&base_result, &result) { (ValidationResult::Approved, ValidationResult::Approved) => { - Ok(ValidationResult::Approved) + approve!() } (ValidationResult::Approved, ValidationResult::Rejected) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Rejected, ValidationResult::Approved) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Rejected, ValidationResult::Rejected) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Pass, _) => Ok(result), - (ValidationResult::ForceApproved, _) => Ok(ValidationResult::ForceApproved), + (ValidationResult::ForceApproved, _) => force_approve!(), (_, ValidationResult::Pass) => Ok(base_result), - (_, ValidationResult::ForceApproved) => Ok(ValidationResult::ForceApproved), + (_, ValidationResult::ForceApproved) => force_approve!(), } } @@ -723,21 +721,21 @@ impl Plugin { match (&base_result, &result) { (ValidationResult::Approved, ValidationResult::Approved) => { - Ok(ValidationResult::Approved) + approve!() } (ValidationResult::Approved, ValidationResult::Rejected) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Rejected, ValidationResult::Approved) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Rejected, ValidationResult::Rejected) => { - Ok(ValidationResult::Rejected) + reject!() } (ValidationResult::Pass, _) => Ok(result), - (ValidationResult::ForceApproved, _) => Ok(ValidationResult::ForceApproved), + (ValidationResult::ForceApproved, _) => force_approve!(), (_, ValidationResult::Pass) => Ok(base_result), - (_, ValidationResult::ForceApproved) => Ok(ValidationResult::ForceApproved), + (_, ValidationResult::ForceApproved) => force_approve!(), } } } @@ -756,6 +754,33 @@ pub enum ValidationResult { ForceApproved, } +// Create a shortcut macro for rejecting a lifecycle action. +macro_rules! reject { + () => {{ + solana_program::msg!("{}:{}:Reject", std::file!(), std::line!()); + Ok(ValidationResult::Rejected) + }}; +} +pub(crate) use reject; + +// Create a shortcut macro for approving a lifecycle action. +macro_rules! approve { + () => {{ + solana_program::msg!("{}:{}:Approve", std::file!(), std::line!()); + Ok(ValidationResult::Approved) + }}; +} +pub(crate) use approve; + +// Create a shortcut macro for approving a lifecycle action. +macro_rules! force_approve { + () => {{ + solana_program::msg!("{}:{}:ForceApprove", std::file!(), std::line!()); + Ok(ValidationResult::ForceApproved) + }}; +} +pub(crate) use force_approve; + /// External plugin adapters lifecycle validations /// External plugin adapters utilize this to indicate whether they approve or reject a lifecycle action. #[derive(Eq, PartialEq, Debug, Clone, BorshDeserialize, BorshSerialize)] @@ -983,15 +1008,15 @@ pub(crate) fn validate_plugin_checks<'a>( ValidationResult::Rejected => rejected = true, ValidationResult::Approved => approved = true, ValidationResult::Pass => continue, - ValidationResult::ForceApproved => return Ok(ValidationResult::ForceApproved), + ValidationResult::ForceApproved => return force_approve!(), } } } if rejected { - Ok(ValidationResult::Rejected) + reject!() } else if approved { - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -1050,7 +1075,7 @@ pub(crate) fn validate_external_plugin_adapter_checks<'a>( match result { ValidationResult::Rejected => { if check_result.can_reject() { - return Ok(ValidationResult::Rejected); + return reject!(); } } ValidationResult::Approved => { @@ -1066,7 +1091,7 @@ pub(crate) fn validate_external_plugin_adapter_checks<'a>( } if approved { - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/permanent_burn_delegate.rs b/programs/mpl-core/src/plugins/permanent_burn_delegate.rs index 5b3e06b7..dc40fb37 100644 --- a/programs/mpl-core/src/plugins/permanent_burn_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_burn_delegate.rs @@ -3,7 +3,10 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; -use super::{PluginType, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, + ValidationResult, +}; /// The permanent burn plugin allows any authority to burn the asset. /// The default authority for this plugin is the update authority. @@ -31,8 +34,7 @@ impl PluginValidation for PermanentBurnDelegate { if ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentBurnDelegate { - solana_program::msg!("PermanentBurnDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -42,8 +44,7 @@ impl PluginValidation for PermanentBurnDelegate { &self, _ctx: &PluginValidationContext, ) -> Result { - solana_program::msg!("PermanentBurnDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } fn validate_burn( @@ -52,8 +53,7 @@ impl PluginValidation for PermanentBurnDelegate { ) -> Result { if let Some(resolved_authorities) = ctx.resolved_authorities { if resolved_authorities.contains(ctx.self_authority) { - solana_program::msg!("PermanentBurnDelegate: ForceApproved"); - return Ok(ValidationResult::ForceApproved); + return force_approve!(); } } diff --git a/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs b/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs index f96121c0..eae82798 100644 --- a/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs @@ -2,11 +2,11 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::PluginType, + plugins::{reject, PluginType}, state::{Authority, DataBlob}, }; -use super::{PluginValidation, PluginValidationContext, ValidationResult}; +use super::{approve, PluginValidation, PluginValidationContext, ValidationResult}; /// The permanent freeze plugin allows any authority to lock the asset so it's no longer transferable. /// The default authority for this plugin is the update authority. @@ -46,8 +46,7 @@ impl PluginValidation for PermanentFreezeDelegate { _ctx: &PluginValidationContext, ) -> Result { if self.frozen { - solana_program::msg!("PermanentFreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -58,8 +57,7 @@ impl PluginValidation for PermanentFreezeDelegate { _ctx: &PluginValidationContext, ) -> Result { if self.frozen { - solana_program::msg!("PermanentFreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -74,8 +72,7 @@ impl PluginValidation for PermanentFreezeDelegate { if ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentFreezeDelegate { - solana_program::msg!("PermanentFreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -93,8 +90,7 @@ impl PluginValidation for PermanentFreezeDelegate { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentFreezeDelegate { - solana_program::msg!("PermanentFreezeDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -106,8 +102,7 @@ impl PluginValidation for PermanentFreezeDelegate { ctx: &PluginValidationContext, ) -> Result { if ctx.target_plugin.is_some() && self.frozen { - solana_program::msg!("PermanentFreezeDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs b/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs index dc11b418..9d94551f 100644 --- a/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs @@ -3,7 +3,10 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; -use super::{PluginType, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, + ValidationResult, +}; /// The permanent transfer plugin allows any authority to transfer the asset. /// The default authority for this plugin is the update authority. @@ -31,8 +34,7 @@ impl PluginValidation for PermanentTransferDelegate { if ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::PermanentTransferDelegate { - solana_program::msg!("PermanentTransferDelegate: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -42,8 +44,7 @@ impl PluginValidation for PermanentTransferDelegate { &self, _ctx: &PluginValidationContext, ) -> Result { - solana_program::msg!("PermanentTransferDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } fn validate_transfer( @@ -52,8 +53,7 @@ impl PluginValidation for PermanentTransferDelegate { ) -> Result { if let Some(resolved_authorities) = ctx.resolved_authorities { if resolved_authorities.contains(ctx.self_authority) { - solana_program::msg!("PermanentTransferDelegate: ForceApproved"); - return Ok(ValidationResult::ForceApproved); + return force_approve!(); } } diff --git a/programs/mpl-core/src/plugins/royalties.rs b/programs/mpl-core/src/plugins/royalties.rs index c79e65cb..cc8e208d 100644 --- a/programs/mpl-core/src/plugins/royalties.rs +++ b/programs/mpl-core/src/plugins/royalties.rs @@ -5,7 +5,7 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; -use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] @@ -85,16 +85,14 @@ impl PluginValidation for Royalties { { Ok(ValidationResult::Pass) } else { - solana_program::msg!("Royalties: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } } RuleSet::ProgramDenyList(deny_list) => { if deny_list.contains(ctx.authority_info.owner) || deny_list.contains(new_owner.owner) { - solana_program::msg!("Royalties: Rejected"); - Ok(ValidationResult::Rejected) + reject!() } else { Ok(ValidationResult::Pass) } @@ -142,8 +140,7 @@ impl PluginValidation for Royalties { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Royalties { - solana_program::msg!("Royalties: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/transfer.rs b/programs/mpl-core/src/plugins/transfer.rs index ca9068e1..548a7fda 100644 --- a/programs/mpl-core/src/plugins/transfer.rs +++ b/programs/mpl-core/src/plugins/transfer.rs @@ -6,7 +6,7 @@ use crate::{ state::{Authority, DataBlob}, }; -use super::{PluginValidation, PluginValidationContext, ValidationResult}; +use super::{approve, PluginValidation, PluginValidationContext, ValidationResult}; /// This plugin manages the ability to transfer an asset and any authorities /// approved are permitted to transfer the asset on behalf of the owner. @@ -47,8 +47,7 @@ impl PluginValidation for TransferDelegate { address: *ctx.authority_info.key, }) { - solana_program::msg!("TransferDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -63,8 +62,7 @@ impl PluginValidation for TransferDelegate { address: *ctx.authority_info.key, }) { - solana_program::msg!("TransferDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -82,8 +80,7 @@ impl PluginValidation for TransferDelegate { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::TransferDelegate { - solana_program::msg!("TransferDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/plugins/update_delegate.rs b/programs/mpl-core/src/plugins/update_delegate.rs index be07616b..3dd1c23c 100644 --- a/programs/mpl-core/src/plugins/update_delegate.rs +++ b/programs/mpl-core/src/plugins/update_delegate.rs @@ -7,7 +7,7 @@ use crate::{ state::{Authority, DataBlob}, }; -use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; /// This plugin manages additional permissions to burn. /// Any authorities approved are given permission to burn the asset on behalf of the owner. @@ -71,8 +71,7 @@ impl PluginValidation for UpdateDelegate { }) && new_plugin.manager() == Authority::UpdateAuthority { - solana_program::msg!("UpdateDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -92,8 +91,7 @@ impl PluginValidation for UpdateDelegate { }) && plugin_to_remove.manager() == Authority::UpdateAuthority { - solana_program::msg!("UpdateDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -114,8 +112,7 @@ impl PluginValidation for UpdateDelegate { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::UpdateDelegate { - solana_program::msg!("UpdateDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -130,8 +127,7 @@ impl PluginValidation for UpdateDelegate { address: *ctx.authority_info.key, }) { - solana_program::msg!("UpdateDelegate: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/state/asset.rs b/programs/mpl-core/src/state/asset.rs index 6134b857..26f91585 100644 --- a/programs/mpl-core/src/state/asset.rs +++ b/programs/mpl-core/src/state/asset.rs @@ -8,7 +8,7 @@ use std::mem::size_of; use crate::{ error::MplCoreError, - plugins::{CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, state::{Compressible, CompressionProof, DataBlob, Key, SolanaAccount}, }; @@ -146,8 +146,7 @@ impl AssetV1 { || (UpdateAuthority::Address(*authority_info.key) == self.update_authority && new_plugin.manager() == Authority::UpdateAuthority) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -169,8 +168,7 @@ impl AssetV1 { && self.update_authority == UpdateAuthority::Address(*authority_info.key)) || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -198,8 +196,7 @@ impl AssetV1 { && self.update_authority == UpdateAuthority::Address(*authority_info.key)) || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -220,8 +217,7 @@ impl AssetV1 { && self.update_authority == UpdateAuthority::Address(*authority_info.key)) || (plugin.manager() == Authority::Owner && authority_info.key == &self.owner) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -238,8 +234,7 @@ impl AssetV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.update_authority.key() { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -253,8 +248,7 @@ impl AssetV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.owner { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -268,8 +262,7 @@ impl AssetV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.owner { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -283,8 +276,7 @@ impl AssetV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.owner { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -298,8 +290,7 @@ impl AssetV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.owner { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -314,8 +305,7 @@ impl AssetV1 { ) -> Result { // If it's not in a collection, then it can be added. if UpdateAuthority::Address(*authority_info.key) == self.update_authority { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -329,8 +319,7 @@ impl AssetV1 { _plugin_to_remove: Option<&ExternalPluginAdapter>, ) -> Result { if self.update_authority == UpdateAuthority::Address(*authority_info.key) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -344,8 +333,7 @@ impl AssetV1 { _plugin: Option<&ExternalPluginAdapter>, ) -> Result { if self.update_authority == UpdateAuthority::Address(*authority_info.key) { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/state/collection.rs b/programs/mpl-core/src/state/collection.rs index 82da9d7f..ccd92a4c 100644 --- a/programs/mpl-core/src/state/collection.rs +++ b/programs/mpl-core/src/state/collection.rs @@ -4,7 +4,7 @@ use solana_program::{account_info::AccountInfo, program_error::ProgramError, pub use crate::{ error::MplCoreError, - plugins::{CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, }; use super::{Authority, CoreAsset, DataBlob, Key, SolanaAccount, UpdateAuthority}; @@ -128,8 +128,7 @@ impl CollectionV1 { if *authority_info.key == self.update_authority && new_plugin.manager() == Authority::UpdateAuthority { - solana_program::msg!("Collection: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -150,8 +149,7 @@ impl CollectionV1 { if *authority_info.key == self.update_authority && plugin_to_remove.manager() == Authority::UpdateAuthority { - solana_program::msg!("Collection: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -182,8 +180,7 @@ impl CollectionV1 { if *authority_info.key == self.update_authority && plugin.manager() == Authority::UpdateAuthority { - solana_program::msg!("Collection: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -204,8 +201,7 @@ impl CollectionV1 { if *authority_info.key == self.update_authority && plugin.manager() == Authority::UpdateAuthority { - solana_program::msg!("Collection: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -239,8 +235,7 @@ impl CollectionV1 { _: Option<&ExternalPluginAdapter>, ) -> Result { if authority_info.key == &self.update_authority { - solana_program::msg!("Collection: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -275,8 +270,7 @@ impl CollectionV1 { ) -> Result { // Approve if the update authority matches the authority. if *authority_info.key == self.update_authority { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -290,8 +284,7 @@ impl CollectionV1 { _plugin_to_remove: Option<&ExternalPluginAdapter>, ) -> Result { if self.update_authority == *authority_info.key { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } @@ -305,8 +298,7 @@ impl CollectionV1 { _plugin: Option<&ExternalPluginAdapter>, ) -> Result { if self.update_authority == *authority_info.key { - solana_program::msg!("Asset: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } diff --git a/programs/mpl-core/src/state/update_authority.rs b/programs/mpl-core/src/state/update_authority.rs index 800163a0..612a0542 100644 --- a/programs/mpl-core/src/state/update_authority.rs +++ b/programs/mpl-core/src/state/update_authority.rs @@ -8,7 +8,9 @@ use crate::{ BurnV1Accounts, CompressV1Accounts, CreateV2Accounts, DecompressV1Accounts, TransferV1Accounts, UpdateV1Accounts, }, - plugins::{fetch_plugin, CheckResult, PluginType, UpdateDelegate, ValidationResult}, + plugins::{ + approve, fetch_plugin, reject, CheckResult, PluginType, UpdateDelegate, ValidationResult, + }, processor::CreateV2Args, state::{Authority, CollectionV1, SolanaAccount}, utils::assert_collection_authority, @@ -82,11 +84,11 @@ impl UpdateAuthority { .is_err() { solana_program::msg!("UA: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } } else if authority_info.key != &collection.update_authority { solana_program::msg!("UA: Rejected"); - return Ok(ValidationResult::Rejected); + return reject!(); } Ok(ValidationResult::Pass) @@ -94,7 +96,7 @@ impl UpdateAuthority { // If you're not trying add a collection, then just pass. (_, UpdateAuthority::Address(_)) => Ok(ValidationResult::Pass), // Otherwise reject because you're doing something weird. - _ => Ok(ValidationResult::Rejected), + _ => reject!(), } } @@ -104,14 +106,13 @@ impl UpdateAuthority { ctx: &UpdateV1Accounts, ) -> Result { let authority = match self { - Self::None => return Ok(ValidationResult::Rejected), + Self::None => return reject!(), Self::Address(address) => address, Self::Collection(address) => address, }; if ctx.authority.unwrap_or(ctx.payer).key == authority { - solana_program::msg!("UA: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { Ok(ValidationResult::Pass) } From c2794be57b7bc2b767e7d8fb2cbdf469a2413492 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Wed, 5 Jun 2024 14:29:29 -0400 Subject: [PATCH 2/3] merging in main --- clients/js/src/generated/errors/mplCore.ts | 26 + clients/js/src/generated/types/autograph.ts | 29 + .../src/generated/types/autographSignature.ts | 32 + clients/js/src/generated/types/index.ts | 4 + clients/js/src/generated/types/plugin.ts | 34 +- clients/js/src/generated/types/pluginType.ts | 2 + .../src/generated/types/verifiedCreators.ts | 34 + .../types/verifiedCreatorsSignature.ts | 35 ++ clients/js/src/plugins/types.ts | 18 +- .../js/test/plugins/asset/autograph.test.ts | 591 ++++++++++++++++++ .../plugins/asset/verifiedCreators.test.ts | 575 +++++++++++++++++ clients/rust/src/generated/errors/mpl_core.rs | 6 + clients/rust/src/generated/types/autograph.rs | 20 + .../generated/types/autograph_signature.rs | 25 + clients/rust/src/generated/types/mod.rs | 8 + clients/rust/src/generated/types/plugin.rs | 4 + .../rust/src/generated/types/plugin_type.rs | 2 + .../src/generated/types/verified_creators.rs | 20 + .../types/verified_creators_signature.rs | 25 + clients/rust/src/hooked/advanced_types.rs | 17 +- clients/rust/src/hooked/mod.rs | 2 + clients/rust/src/hooked/plugin.rs | 21 +- idls/mpl_core.json | 96 +++ programs/mpl-core/src/error.rs | 8 + programs/mpl-core/src/plugins/autograph.rs | 141 +++++ programs/mpl-core/src/plugins/edition.rs | 26 +- programs/mpl-core/src/plugins/lifecycle.rs | 69 +- programs/mpl-core/src/plugins/mod.rs | 16 + programs/mpl-core/src/plugins/royalties.rs | 7 +- .../mpl-core/src/plugins/verified_creators.rs | 218 +++++++ programs/mpl-core/src/processor/add_plugin.rs | 3 +- 31 files changed, 2081 insertions(+), 33 deletions(-) create mode 100644 clients/js/src/generated/types/autograph.ts create mode 100644 clients/js/src/generated/types/autographSignature.ts create mode 100644 clients/js/src/generated/types/verifiedCreators.ts create mode 100644 clients/js/src/generated/types/verifiedCreatorsSignature.ts create mode 100644 clients/js/test/plugins/asset/autograph.test.ts create mode 100644 clients/js/test/plugins/asset/verifiedCreators.test.ts create mode 100644 clients/rust/src/generated/types/autograph.rs create mode 100644 clients/rust/src/generated/types/autograph_signature.rs create mode 100644 clients/rust/src/generated/types/verified_creators.rs create mode 100644 clients/rust/src/generated/types/verified_creators_signature.rs create mode 100644 programs/mpl-core/src/plugins/autograph.rs create mode 100644 programs/mpl-core/src/plugins/verified_creators.rs diff --git a/clients/js/src/generated/errors/mplCore.ts b/clients/js/src/generated/errors/mplCore.ts index a634ff54..a70010f3 100644 --- a/clients/js/src/generated/errors/mplCore.ts +++ b/clients/js/src/generated/errors/mplCore.ts @@ -582,6 +582,32 @@ nameToErrorMap.set( UninitializedOracleAccountError ); +/** MissingSigner: Missing required signer for operation */ +export class MissingSignerError extends ProgramError { + override readonly name: string = 'MissingSigner'; + + readonly code: number = 0x28; // 40 + + constructor(program: Program, cause?: Error) { + super('Missing required signer for operation', program, cause); + } +} +codeToErrorMap.set(0x28, MissingSignerError); +nameToErrorMap.set('MissingSigner', MissingSignerError); + +/** InvalidPluginOperation: Invalid plugin operation */ +export class InvalidPluginOperationError extends ProgramError { + override readonly name: string = 'InvalidPluginOperation'; + + readonly code: number = 0x29; // 41 + + constructor(program: Program, cause?: Error) { + super('Invalid plugin operation', program, cause); + } +} +codeToErrorMap.set(0x29, InvalidPluginOperationError); +nameToErrorMap.set('InvalidPluginOperation', InvalidPluginOperationError); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/clients/js/src/generated/types/autograph.ts b/clients/js/src/generated/types/autograph.ts new file mode 100644 index 00000000..c3e74716 --- /dev/null +++ b/clients/js/src/generated/types/autograph.ts @@ -0,0 +1,29 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Serializer, + array, + struct, +} from '@metaplex-foundation/umi/serializers'; +import { + AutographSignature, + AutographSignatureArgs, + getAutographSignatureSerializer, +} from '.'; + +export type Autograph = { signatures: Array }; + +export type AutographArgs = { signatures: Array }; + +export function getAutographSerializer(): Serializer { + return struct( + [['signatures', array(getAutographSignatureSerializer())]], + { description: 'Autograph' } + ) as Serializer; +} diff --git a/clients/js/src/generated/types/autographSignature.ts b/clients/js/src/generated/types/autographSignature.ts new file mode 100644 index 00000000..f60603e8 --- /dev/null +++ b/clients/js/src/generated/types/autographSignature.ts @@ -0,0 +1,32 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { PublicKey } from '@metaplex-foundation/umi'; +import { + Serializer, + publicKey as publicKeySerializer, + string, + struct, +} from '@metaplex-foundation/umi/serializers'; + +export type AutographSignature = { address: PublicKey; message: string }; + +export type AutographSignatureArgs = AutographSignature; + +export function getAutographSignatureSerializer(): Serializer< + AutographSignatureArgs, + AutographSignature +> { + return struct( + [ + ['address', publicKeySerializer()], + ['message', string()], + ], + { description: 'AutographSignature' } + ) as Serializer; +} diff --git a/clients/js/src/generated/types/index.ts b/clients/js/src/generated/types/index.ts index ca171c8e..c744b4f0 100644 --- a/clients/js/src/generated/types/index.ts +++ b/clients/js/src/generated/types/index.ts @@ -9,6 +9,8 @@ export * from './addBlocker'; export * from './attribute'; export * from './attributes'; +export * from './autograph'; +export * from './autographSignature'; export * from './baseDataStore'; export * from './baseDataStoreInitInfo'; export * from './baseDataStoreUpdateInfo'; @@ -57,3 +59,5 @@ export * from './registryRecord'; export * from './transferDelegate'; export * from './updateDelegate'; export * from './validationResult'; +export * from './verifiedCreators'; +export * from './verifiedCreatorsSignature'; diff --git a/clients/js/src/generated/types/plugin.ts b/clients/js/src/generated/types/plugin.ts index 64c4c394..b5e2a9bb 100644 --- a/clients/js/src/generated/types/plugin.ts +++ b/clients/js/src/generated/types/plugin.ts @@ -19,6 +19,8 @@ import { AddBlockerArgs, Attributes, AttributesArgs, + Autograph, + AutographArgs, BaseMasterEdition, BaseMasterEditionArgs, BaseRoyalties, @@ -41,8 +43,11 @@ import { TransferDelegateArgs, UpdateDelegate, UpdateDelegateArgs, + VerifiedCreators, + VerifiedCreatorsArgs, getAddBlockerSerializer, getAttributesSerializer, + getAutographSerializer, getBaseMasterEditionSerializer, getBaseRoyaltiesSerializer, getBurnDelegateSerializer, @@ -54,6 +59,7 @@ import { getPermanentTransferDelegateSerializer, getTransferDelegateSerializer, getUpdateDelegateSerializer, + getVerifiedCreatorsSerializer, } from '.'; export type Plugin = @@ -69,7 +75,9 @@ export type Plugin = | { __kind: 'Edition'; fields: [Edition] } | { __kind: 'MasterEdition'; fields: [BaseMasterEdition] } | { __kind: 'AddBlocker'; fields: [AddBlocker] } - | { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadata] }; + | { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadata] } + | { __kind: 'VerifiedCreators'; fields: [VerifiedCreators] } + | { __kind: 'Autograph'; fields: [Autograph] }; export type PluginArgs = | { __kind: 'Royalties'; fields: [BaseRoyaltiesArgs] } @@ -87,7 +95,9 @@ export type PluginArgs = | { __kind: 'Edition'; fields: [EditionArgs] } | { __kind: 'MasterEdition'; fields: [BaseMasterEditionArgs] } | { __kind: 'AddBlocker'; fields: [AddBlockerArgs] } - | { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadataArgs] }; + | { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadataArgs] } + | { __kind: 'VerifiedCreators'; fields: [VerifiedCreatorsArgs] } + | { __kind: 'Autograph'; fields: [AutographArgs] }; export function getPluginSerializer(): Serializer { return dataEnum( @@ -170,6 +180,18 @@ export function getPluginSerializer(): Serializer { ['fields', tuple([getImmutableMetadataSerializer()])], ]), ], + [ + 'VerifiedCreators', + struct>([ + ['fields', tuple([getVerifiedCreatorsSerializer()])], + ]), + ], + [ + 'Autograph', + struct>([ + ['fields', tuple([getAutographSerializer()])], + ]), + ], ], { description: 'Plugin' } ) as Serializer; @@ -231,6 +253,14 @@ export function plugin( kind: 'ImmutableMetadata', data: GetDataEnumKindContent['fields'] ): GetDataEnumKind; +export function plugin( + kind: 'VerifiedCreators', + data: GetDataEnumKindContent['fields'] +): GetDataEnumKind; +export function plugin( + kind: 'Autograph', + data: GetDataEnumKindContent['fields'] +): GetDataEnumKind; export function plugin( kind: K, data?: any diff --git a/clients/js/src/generated/types/pluginType.ts b/clients/js/src/generated/types/pluginType.ts index 759a518a..a001175d 100644 --- a/clients/js/src/generated/types/pluginType.ts +++ b/clients/js/src/generated/types/pluginType.ts @@ -22,6 +22,8 @@ export enum PluginType { MasterEdition, AddBlocker, ImmutableMetadata, + VerifiedCreators, + Autograph, } export type PluginTypeArgs = PluginType; diff --git a/clients/js/src/generated/types/verifiedCreators.ts b/clients/js/src/generated/types/verifiedCreators.ts new file mode 100644 index 00000000..62109cbb --- /dev/null +++ b/clients/js/src/generated/types/verifiedCreators.ts @@ -0,0 +1,34 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Serializer, + array, + struct, +} from '@metaplex-foundation/umi/serializers'; +import { + VerifiedCreatorsSignature, + VerifiedCreatorsSignatureArgs, + getVerifiedCreatorsSignatureSerializer, +} from '.'; + +export type VerifiedCreators = { signatures: Array }; + +export type VerifiedCreatorsArgs = { + signatures: Array; +}; + +export function getVerifiedCreatorsSerializer(): Serializer< + VerifiedCreatorsArgs, + VerifiedCreators +> { + return struct( + [['signatures', array(getVerifiedCreatorsSignatureSerializer())]], + { description: 'VerifiedCreators' } + ) as Serializer; +} diff --git a/clients/js/src/generated/types/verifiedCreatorsSignature.ts b/clients/js/src/generated/types/verifiedCreatorsSignature.ts new file mode 100644 index 00000000..ee93d3c3 --- /dev/null +++ b/clients/js/src/generated/types/verifiedCreatorsSignature.ts @@ -0,0 +1,35 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { PublicKey } from '@metaplex-foundation/umi'; +import { + Serializer, + bool, + publicKey as publicKeySerializer, + struct, +} from '@metaplex-foundation/umi/serializers'; + +export type VerifiedCreatorsSignature = { + address: PublicKey; + verified: boolean; +}; + +export type VerifiedCreatorsSignatureArgs = VerifiedCreatorsSignature; + +export function getVerifiedCreatorsSignatureSerializer(): Serializer< + VerifiedCreatorsSignatureArgs, + VerifiedCreatorsSignature +> { + return struct( + [ + ['address', publicKeySerializer()], + ['verified', bool()], + ], + { description: 'VerifiedCreatorsSignature' } + ) as Serializer; +} diff --git a/clients/js/src/plugins/types.ts b/clients/js/src/plugins/types.ts index 74ccdb40..bc5d4a1f 100644 --- a/clients/js/src/plugins/types.ts +++ b/clients/js/src/plugins/types.ts @@ -21,6 +21,10 @@ import { BaseMasterEditionArgs, AddBlocker, ImmutableMetadata, + AutographArgs, + VerifiedCreatorsArgs, + Autograph, + VerifiedCreators, } from '../generated'; import { RoyaltiesArgs, RoyaltiesPlugin } from './royalties'; import { PluginAuthority } from './pluginAuthority'; @@ -113,7 +117,10 @@ export type OwnerManagedPluginArgsV2 = } | { type: 'TransferDelegate'; - }; + } + | ({ + type: 'Autograph'; + } & AutographArgs); export type AuthorityManagedPluginArgsV2 = | ({ @@ -133,7 +140,10 @@ export type AuthorityManagedPluginArgsV2 = } | { type: 'AddBlocker'; - }; + } + | ({ + type: 'VerifiedCreators'; + } & VerifiedCreatorsArgs); export type AssetAddablePluginArgsV2 = | OwnerManagedPluginArgsV2 @@ -169,6 +179,8 @@ export type EditionPlugin = BasePlugin & Edition; export type MasterEditionPlugin = BasePlugin & MasterEdition; export type AddBlockerPlugin = BasePlugin & AddBlocker; export type ImmutableMetadataPlugin = BasePlugin & ImmutableMetadata; +export type VerifiedCreatorsPlugin = BasePlugin & VerifiedCreators; +export type AutographPlugin = BasePlugin & Autograph; export type CommonPluginsList = { attributes?: AttributesPlugin; @@ -179,6 +191,8 @@ export type CommonPluginsList = { permanentBurnDelegate?: PermanentBurnDelegatePlugin; addBlocker?: AddBlockerPlugin; immutableMetadata?: ImmutableMetadataPlugin; + autograph?: AutographPlugin; + verifiedCreators?: VerifiedCreatorsPlugin; }; export type AssetPluginsList = { diff --git a/clients/js/test/plugins/asset/autograph.test.ts b/clients/js/test/plugins/asset/autograph.test.ts new file mode 100644 index 00000000..a6dd92e2 --- /dev/null +++ b/clients/js/test/plugins/asset/autograph.test.ts @@ -0,0 +1,591 @@ +import test from 'ava'; + +import { generateSigner } from '@metaplex-foundation/umi'; +import { generateSignerWithSol } from '@metaplex-foundation/umi-bundle-tests'; +import { DEFAULT_ASSET, assertAsset, createUmi } from '../../_setupRaw'; +import { createAsset } from '../../_setupSdk'; +import { addPlugin, updatePlugin } from '../../../src'; + +test('it can create asset with autograph plugin', async (t) => { + const umi = await createUmi(); + + const asset = await createAsset(umi, { + plugins: [ + { + type: 'Autograph', + signatures: [], + }, + ], + }); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [], + }, + }); +}); + +test('it cannot create asset with autograph plugin and unauthorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const autograph = generateSigner(umi); + + const res = createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: autograph.publicKey, + message: 'hi', + }, + ], + }, + ], + }); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it can create asset with autograph plugin with authorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'hi', + }, + ], + }, + ], + }); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [ + { + address: umi.identity.publicKey, + message: 'hi', + }, + ], + }, + }); +}); + +test('it cannot add autograph plugin to asset by creator', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + }); + + const res = addPlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'NoApprovals' }); +}); + +test('it can add autograph plugin to asset by owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + }); + + await addPlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + authority: owner, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [ + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + }); +}); + +test('it cannot add autograph plugin to asset by 3rd party', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + const autograph = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + }); + + const res = addPlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: autograph.publicKey, + message: 'autograph', + }, + ], + }, + authority: autograph, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'NoApprovals' }); +}); + +test('it cannot add autograph to asset by unauthorized 3rd party', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + const unauthed = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + authority: unauthed, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it can add additional autograph to asset via update by 3rd party', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + const autograph = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + { + address: autograph.publicKey, + message: 'autograph', + }, + ], + }, + authority: autograph, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + { + address: autograph.publicKey, + message: 'autograph', + }, + ], + }, + }); +}); + +test('it can remove autograph if owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [], + }, + authority: owner, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [], + }, + }); +}); + +test('it cannot remove autograph if not owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + const autograph = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [], + }, + authority: autograph, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it cannot modify autograph message as signer', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator2', + }, + ], + }, + authority: umi.identity, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'InvalidPluginOperation' }); +}); + +test('it cannot modify autograph message as owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + authority: owner, + }).sendAndConfirm(umi); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator2', + }, + { + address: owner.publicKey, + message: 'owner2', + }, + ], + }, + authority: owner, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'InvalidPluginOperation' }); +}); + +test('it can remove and add autographs as owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + authority: owner, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Owner', + }, + signatures: [ + { + address: owner.publicKey, + message: 'owner', + }, + ], + }, + }); +}); + +test('it can remove and add autographs as delegate', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + const delegate = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + ], + authority: { + type: 'Address', + address: delegate.publicKey, + }, + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'Autograph', + signatures: [ + { + address: delegate.publicKey, + message: 'delegate', + }, + ], + }, + authority: delegate, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + autograph: { + authority: { + type: 'Address', + address: delegate.publicKey, + }, + signatures: [ + { + address: delegate.publicKey, + message: 'delegate', + }, + ], + }, + }); +}); + +test('it cannot add duplicate autographs', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const res = createAsset(umi, { + owner, + plugins: [ + { + type: 'Autograph', + signatures: [ + { + address: umi.identity.publicKey, + message: 'creator', + }, + { + address: umi.identity.publicKey, + message: 'creator2', + }, + ], + }, + ], + }); + + await t.throwsAsync(res, { name: 'InvalidPluginSetting' }); +}); diff --git a/clients/js/test/plugins/asset/verifiedCreators.test.ts b/clients/js/test/plugins/asset/verifiedCreators.test.ts new file mode 100644 index 00000000..6e4a7ccf --- /dev/null +++ b/clients/js/test/plugins/asset/verifiedCreators.test.ts @@ -0,0 +1,575 @@ +import test from 'ava'; + +import { generateSigner } from '@metaplex-foundation/umi'; +import { generateSignerWithSol } from '@metaplex-foundation/umi-bundle-tests'; +import { DEFAULT_ASSET, assertAsset, createUmi } from '../../_setupRaw'; +import { createAsset } from '../../_setupSdk'; +import { addPlugin, updatePlugin } from '../../../src'; + +test('it can create asset with verified creators plugin', async (t) => { + const umi = await createUmi(); + + const asset = await createAsset(umi, { + plugins: [ + { + type: 'VerifiedCreators', + signatures: [], + }, + ], + }); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [], + }, + }); +}); + +test('it cannot create asset with verified creators plugin and unauthorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + + const res = createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + ], + }); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it can create asset with verified creators plugin with authorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + ], + }, + ], + }); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + ], + }, + }); +}); + +test('it cannot add verified creator plugin to asset by owner', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const asset = await createAsset(umi, { + owner, + }); + + const res = addPlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: owner.publicKey, + verified: true, + }, + ], + }, + authority: owner, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'NoApprovals' }); +}); + +test('it can create asset with verified creators plugin with unverified signatures and then verify', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: false, + }, + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: umi.identity.publicKey, + verified: false, + }, + { + address: creator.publicKey, + verified: false, + }, + ], + }, + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: false, + }, + { + address: creator.publicKey, + verified: true, + }, + ], + }, + authority: creator, + }).sendAndConfirm(umi); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + { + address: creator.publicKey, + verified: true, + }, + ], + }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + { + address: creator.publicKey, + verified: true, + }, + ], + }, + }); +}); + +test('it can unverify signature verified creator plugin', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + authority: creator, + }).sendAndConfirm(umi); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + authority: creator, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + }); +}); + +test('it cannot verify a verified creator plugin with unauthorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + const unauthed = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + authority: unauthed, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it cannot remove verified creator plugin signture with unauthorized signature', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + const unauthed = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [], + }, + authority: unauthed, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'MissingSigner' }); +}); + +test('it can remove and add unverified creator plugin signature with update auth', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + const creator2 = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + { + address: creator2.publicKey, + verified: false, + }, + ], + }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: umi.identity.publicKey, + verified: true, + }, + ], + }, + }); +}); + +test('it cannot remove verified creator plugin signature with update auth', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + authority: creator, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [], + }, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'InvalidPluginOperation' }); +}); + +test('it cannot unverify verified creator plugin signature with update auth', async (t) => { + const umi = await createUmi(); + const owner = generateSigner(umi); + const creator = generateSigner(umi); + + const asset = await createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + authority: creator, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: owner.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + verifiedCreators: { + authority: { + type: 'UpdateAuthority', + }, + signatures: [ + { + address: creator.publicKey, + verified: true, + }, + ], + }, + }); + + const res = updatePlugin(umi, { + asset: asset.publicKey, + plugin: { + type: 'VerifiedCreators', + signatures: [ + { + address: creator.publicKey, + verified: false, + }, + ], + }, + }).sendAndConfirm(umi); + + await t.throwsAsync(res, { name: 'InvalidPluginOperation' }); +}); + +test('it cannot add duplicate verified creator signatures', async (t) => { + const umi = await createUmi(); + const owner = await generateSignerWithSol(umi); + + const res = createAsset(umi, { + owner, + plugins: [ + { + type: 'VerifiedCreators', + signatures: [ + { + address: umi.identity.publicKey, + verified: false, + }, + { + address: umi.identity.publicKey, + verified: false, + }, + ], + }, + ], + }); + + await t.throwsAsync(res, { name: 'InvalidPluginSetting' }); +}); diff --git a/clients/rust/src/generated/errors/mpl_core.rs b/clients/rust/src/generated/errors/mpl_core.rs index e079f3f4..e7ef105f 100644 --- a/clients/rust/src/generated/errors/mpl_core.rs +++ b/clients/rust/src/generated/errors/mpl_core.rs @@ -130,6 +130,12 @@ pub enum MplCoreError { /// 39 (0x27) - Oracle account is uninitialized #[error("Oracle account is uninitialized")] UninitializedOracleAccount, + /// 40 (0x28) - Missing required signer for operation + #[error("Missing required signer for operation")] + MissingSigner, + /// 41 (0x29) - Invalid plugin operation + #[error("Invalid plugin operation")] + InvalidPluginOperation, } impl solana_program::program_error::PrintProgramError for MplCoreError { diff --git a/clients/rust/src/generated/types/autograph.rs b/clients/rust/src/generated/types/autograph.rs new file mode 100644 index 00000000..461d737f --- /dev/null +++ b/clients/rust/src/generated/types/autograph.rs @@ -0,0 +1,20 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use crate::generated::types::AutographSignature; +#[cfg(feature = "anchor")] +use anchor_lang::prelude::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(not(feature = "anchor"), derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "anchor", derive(AnchorSerialize, AnchorDeserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Autograph { + pub signatures: Vec, +} diff --git a/clients/rust/src/generated/types/autograph_signature.rs b/clients/rust/src/generated/types/autograph_signature.rs new file mode 100644 index 00000000..4e9bc2a9 --- /dev/null +++ b/clients/rust/src/generated/types/autograph_signature.rs @@ -0,0 +1,25 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +#[cfg(feature = "anchor")] +use anchor_lang::prelude::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(not(feature = "anchor"), derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "anchor", derive(AnchorSerialize, AnchorDeserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutographSignature { + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub address: Pubkey, + pub message: String, +} diff --git a/clients/rust/src/generated/types/mod.rs b/clients/rust/src/generated/types/mod.rs index 0b2d39bd..1a02a50a 100644 --- a/clients/rust/src/generated/types/mod.rs +++ b/clients/rust/src/generated/types/mod.rs @@ -8,6 +8,8 @@ pub(crate) mod r#add_blocker; pub(crate) mod r#attribute; pub(crate) mod r#attributes; +pub(crate) mod r#autograph; +pub(crate) mod r#autograph_signature; pub(crate) mod r#burn_delegate; pub(crate) mod r#compression_proof; pub(crate) mod r#creator; @@ -56,10 +58,14 @@ pub(crate) mod r#update_authority; pub(crate) mod r#update_delegate; pub(crate) mod r#validation_result; pub(crate) mod r#validation_results_offset; +pub(crate) mod r#verified_creators; +pub(crate) mod r#verified_creators_signature; pub use self::r#add_blocker::*; pub use self::r#attribute::*; pub use self::r#attributes::*; +pub use self::r#autograph::*; +pub use self::r#autograph_signature::*; pub use self::r#burn_delegate::*; pub use self::r#compression_proof::*; pub use self::r#creator::*; @@ -108,3 +114,5 @@ pub use self::r#update_authority::*; pub use self::r#update_delegate::*; pub use self::r#validation_result::*; pub use self::r#validation_results_offset::*; +pub use self::r#verified_creators::*; +pub use self::r#verified_creators_signature::*; diff --git a/clients/rust/src/generated/types/plugin.rs b/clients/rust/src/generated/types/plugin.rs index b6b8db9d..98a923a9 100644 --- a/clients/rust/src/generated/types/plugin.rs +++ b/clients/rust/src/generated/types/plugin.rs @@ -7,6 +7,7 @@ use crate::generated::types::AddBlocker; use crate::generated::types::Attributes; +use crate::generated::types::Autograph; use crate::generated::types::BurnDelegate; use crate::generated::types::Edition; use crate::generated::types::FreezeDelegate; @@ -18,6 +19,7 @@ use crate::generated::types::PermanentTransferDelegate; use crate::generated::types::Royalties; use crate::generated::types::TransferDelegate; use crate::generated::types::UpdateDelegate; +use crate::generated::types::VerifiedCreators; #[cfg(feature = "anchor")] use anchor_lang::prelude::{AnchorDeserialize, AnchorSerialize}; #[cfg(not(feature = "anchor"))] @@ -41,4 +43,6 @@ pub enum Plugin { MasterEdition(MasterEdition), AddBlocker(AddBlocker), ImmutableMetadata(ImmutableMetadata), + VerifiedCreators(VerifiedCreators), + Autograph(Autograph), } diff --git a/clients/rust/src/generated/types/plugin_type.rs b/clients/rust/src/generated/types/plugin_type.rs index 638bf44a..7eb36292 100644 --- a/clients/rust/src/generated/types/plugin_type.rs +++ b/clients/rust/src/generated/types/plugin_type.rs @@ -29,4 +29,6 @@ pub enum PluginType { MasterEdition, AddBlocker, ImmutableMetadata, + VerifiedCreators, + Autograph, } diff --git a/clients/rust/src/generated/types/verified_creators.rs b/clients/rust/src/generated/types/verified_creators.rs new file mode 100644 index 00000000..913c47af --- /dev/null +++ b/clients/rust/src/generated/types/verified_creators.rs @@ -0,0 +1,20 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use crate::generated::types::VerifiedCreatorsSignature; +#[cfg(feature = "anchor")] +use anchor_lang::prelude::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize, BorshSerialize}; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(not(feature = "anchor"), derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "anchor", derive(AnchorSerialize, AnchorDeserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VerifiedCreators { + pub signatures: Vec, +} diff --git a/clients/rust/src/generated/types/verified_creators_signature.rs b/clients/rust/src/generated/types/verified_creators_signature.rs new file mode 100644 index 00000000..242476bd --- /dev/null +++ b/clients/rust/src/generated/types/verified_creators_signature.rs @@ -0,0 +1,25 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +#[cfg(feature = "anchor")] +use anchor_lang::prelude::{AnchorDeserialize, AnchorSerialize}; +#[cfg(not(feature = "anchor"))] +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(not(feature = "anchor"), derive(BorshSerialize, BorshDeserialize))] +#[cfg_attr(feature = "anchor", derive(AnchorSerialize, AnchorDeserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VerifiedCreatorsSignature { + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub address: Pubkey, + pub verified: bool, +} diff --git a/clients/rust/src/hooked/advanced_types.rs b/clients/rust/src/hooked/advanced_types.rs index e3976252..081d35f0 100644 --- a/clients/rust/src/hooked/advanced_types.rs +++ b/clients/rust/src/hooked/advanced_types.rs @@ -8,10 +8,11 @@ use std::{cmp::Ordering, io::ErrorKind}; use crate::{ accounts::{BaseAssetV1, BaseCollectionV1, PluginHeaderV1}, types::{ - AddBlocker, Attributes, BurnDelegate, DataStore, Edition, ExternalCheckResult, + AddBlocker, Attributes, Autograph, BurnDelegate, DataStore, Edition, ExternalCheckResult, ExternalPluginAdapter, ExternalPluginAdapterKey, FreezeDelegate, ImmutableMetadata, Key, LifecycleHook, MasterEdition, Oracle, PermanentBurnDelegate, PermanentFreezeDelegate, PermanentTransferDelegate, PluginAuthority, Royalties, TransferDelegate, UpdateDelegate, + VerifiedCreators, }, }; @@ -147,6 +148,18 @@ pub struct ImmutableMetadataPlugin { pub immutable_metadata: ImmutableMetadata, } +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct VerifiedCreatorsPlugin { + pub base: BasePlugin, + pub verified_creators: VerifiedCreators, +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct AutographPlugin { + pub base: BasePlugin, + pub autograph: Autograph, +} + #[derive(Debug, Default)] pub struct PluginsList { pub royalties: Option, @@ -162,6 +175,8 @@ pub struct PluginsList { pub master_edition: Option, pub add_blocker: Option, pub immutable_metadata: Option, + pub verified_creators: Option, + pub autograph: Option, } #[derive(Debug, Default)] diff --git a/clients/rust/src/hooked/mod.rs b/clients/rust/src/hooked/mod.rs index 0e499d0f..8628a463 100644 --- a/clients/rust/src/hooked/mod.rs +++ b/clients/rust/src/hooked/mod.rs @@ -43,6 +43,8 @@ impl From<&Plugin> for PluginType { Plugin::MasterEdition(_) => PluginType::MasterEdition, Plugin::AddBlocker(_) => PluginType::AddBlocker, Plugin::ImmutableMetadata(_) => PluginType::ImmutableMetadata, + Plugin::VerifiedCreators(_) => PluginType::VerifiedCreators, + Plugin::Autograph(_) => PluginType::Autograph, } } } diff --git a/clients/rust/src/hooked/plugin.rs b/clients/rust/src/hooked/plugin.rs index 283edb4c..63b6f1d4 100644 --- a/clients/rust/src/hooked/plugin.rs +++ b/clients/rust/src/hooked/plugin.rs @@ -12,12 +12,12 @@ use crate::{ ExternalPluginAdapter, ExternalPluginAdapterType, Plugin, PluginAuthority, PluginType, RegistryRecord, }, - AddBlockerPlugin, AttributesPlugin, BaseAuthority, BasePlugin, BurnDelegatePlugin, DataBlob, - EditionPlugin, ExternalPluginAdaptersList, ExternalRegistryRecordSafe, FreezeDelegatePlugin, - ImmutableMetadataPlugin, MasterEditionPlugin, PermanentBurnDelegatePlugin, - PermanentFreezeDelegatePlugin, PermanentTransferDelegatePlugin, PluginRegistryV1Safe, - PluginsList, RegistryRecordSafe, RoyaltiesPlugin, SolanaAccount, TransferDelegatePlugin, - UpdateDelegatePlugin, + AddBlockerPlugin, AttributesPlugin, AutographPlugin, BaseAuthority, BasePlugin, + BurnDelegatePlugin, DataBlob, EditionPlugin, ExternalPluginAdaptersList, + ExternalRegistryRecordSafe, FreezeDelegatePlugin, ImmutableMetadataPlugin, MasterEditionPlugin, + PermanentBurnDelegatePlugin, PermanentFreezeDelegatePlugin, PermanentTransferDelegatePlugin, + PluginRegistryV1Safe, PluginsList, RegistryRecordSafe, RoyaltiesPlugin, SolanaAccount, + TransferDelegatePlugin, UpdateDelegatePlugin, VerifiedCreatorsPlugin, }; /// Fetch the plugin from the registry. @@ -207,6 +207,15 @@ pub(crate) fn registry_records_to_plugin_list( immutable_metadata, }) } + Plugin::VerifiedCreators(verified_creators) => { + acc.verified_creators = Some(VerifiedCreatorsPlugin { + base, + verified_creators, + }) + } + Plugin::Autograph(autograph) => { + acc.autograph = Some(AutographPlugin { base, autograph }) + } } } Ok(acc) diff --git a/idls/mpl_core.json b/idls/mpl_core.json index d775c7a4..acc00a06 100644 --- a/idls/mpl_core.json +++ b/idls/mpl_core.json @@ -2120,6 +2120,38 @@ ] } }, + { + "name": "AutographSignature", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "message", + "type": "string" + } + ] + } + }, + { + "name": "Autograph", + "type": { + "kind": "struct", + "fields": [ + { + "name": "signatures", + "type": { + "vec": { + "defined": "AutographSignature" + } + } + } + ] + } + }, { "name": "BurnDelegate", "type": { @@ -2686,6 +2718,38 @@ ] } }, + { + "name": "VerifiedCreatorsSignature", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "verified", + "type": "bool" + } + ] + } + }, + { + "name": "VerifiedCreators", + "type": { + "kind": "struct", + "fields": [ + { + "name": "signatures", + "type": { + "vec": { + "defined": "VerifiedCreatorsSignature" + } + } + } + ] + } + }, { "name": "AddExternalPluginAdapterV1Args", "type": { @@ -3437,6 +3501,22 @@ "defined": "ImmutableMetadata" } ] + }, + { + "name": "VerifiedCreators", + "fields": [ + { + "defined": "VerifiedCreators" + } + ] + }, + { + "name": "Autograph", + "fields": [ + { + "defined": "Autograph" + } + ] } ] } @@ -3484,6 +3564,12 @@ }, { "name": "ImmutableMetadata" + }, + { + "name": "VerifiedCreators" + }, + { + "name": "Autograph" } ] } @@ -4229,6 +4315,16 @@ "code": 39, "name": "UninitializedOracleAccount", "msg": "Oracle account is uninitialized" + }, + { + "code": 40, + "name": "MissingSigner", + "msg": "Missing required signer for operation" + }, + { + "code": 41, + "name": "InvalidPluginOperation", + "msg": "Invalid plugin operation" } ], "metadata": { diff --git a/programs/mpl-core/src/error.rs b/programs/mpl-core/src/error.rs index 9c02b853..59b19a1f 100644 --- a/programs/mpl-core/src/error.rs +++ b/programs/mpl-core/src/error.rs @@ -168,6 +168,14 @@ pub enum MplCoreError { /// 39 - Oracle account is uninitialized #[error("Oracle account is uninitialized")] UninitializedOracleAccount, + + /// 40 - Missing signer for operation + #[error("Missing required signer for operation")] + MissingSigner, + + /// 41 - Invalid plugin operation + #[error("Invalid plugin operation")] + InvalidPluginOperation, } impl PrintProgramError for MplCoreError { diff --git a/programs/mpl-core/src/plugins/autograph.rs b/programs/mpl-core/src/plugins/autograph.rs new file mode 100644 index 00000000..d0b63a1e --- /dev/null +++ b/programs/mpl-core/src/plugins/autograph.rs @@ -0,0 +1,141 @@ +use std::collections::{BTreeMap, HashSet}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::{program_error::ProgramError, pubkey::Pubkey}; + +use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; + +use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; + +/// The creator on an asset and whether or not they are verified. +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] +pub struct AutographSignature { + address: Pubkey, + message: String, +} + +/// Structure for an autograph book, often used in conjunction with the Royalties plugin for verified creators +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] +pub struct Autograph { + /// A list of signatures with option message + signatures: Vec, +} + +fn validate_autograph( + new_autograph: &Autograph, + autograph: Option<&Autograph>, + authority: &Pubkey, + is_plugin_authority: bool, +) -> Result { + let existing_map = autograph.map_or_else(BTreeMap::new, |autograph| { + autograph + .signatures + .iter() + .map(|sig| (sig.address, sig)) + .collect::>() + }); + + for sig in new_autograph.signatures.iter() { + // only the signing authority can add their own signature + match existing_map.get(&sig.address) { + Some(existing_sig) => { + if existing_sig.message != sig.message { + solana_program::msg!("Autograph: Rejected"); + return Err(MplCoreError::InvalidPluginOperation.into()); + } + } + None => { + if &sig.address != authority { + solana_program::msg!("Autograph: Rejected"); + return Err(MplCoreError::MissingSigner.into()); + } + } + } + } + + let new_signatures: HashSet<_> = new_autograph + .signatures + .iter() + .map(|sig| sig.address) + .collect(); + + if new_autograph.signatures.len() != new_signatures.len() { + solana_program::msg!("Autograph: Rejected"); + return Err(MplCoreError::InvalidPluginSetting.into()); + } + + if !is_plugin_authority { + // only the plugin authority can remove signatures + for (key, _) in existing_map.iter() { + if !new_signatures.contains(key) { + solana_program::msg!("Autograph: Rejected"); + return Err(MplCoreError::MissingSigner.into()); + } + } + } + + Ok(ValidationResult::Pass) +} + +impl PluginValidation for Autograph { + fn validate_create( + &self, + ctx: &PluginValidationContext, + ) -> Result { + validate_autograph(self, None, ctx.authority_info.key, true) + } + + fn validate_add_plugin( + &self, + ctx: &PluginValidationContext, + ) -> Result { + match ctx.target_plugin { + Some(Plugin::Autograph(_autograph)) => { + validate_autograph(self, None, ctx.authority_info.key, true)?; + Ok(ValidationResult::Approved) + } + _ => Ok(ValidationResult::Pass), + } + } + + fn validate_update_plugin( + &self, + ctx: &PluginValidationContext, + ) -> Result { + let resolved_authorities = 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), + )?; + solana_program::msg!("Autograph: Approved"); + Ok(ValidationResult::Approved) + } + _ => Ok(ValidationResult::Pass), + } + } + + /// Validate the revoke plugin authority lifecycle action. + fn validate_revoke_plugin_authority( + &self, + ctx: &PluginValidationContext, + ) -> Result { + if ctx.self_authority + == &(Authority::Address { + address: *ctx.authority_info.key, + }) + && ctx.target_plugin.is_some() + && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Autograph + { + solana_program::msg!("Autograph: Approved"); + Ok(ValidationResult::Approved) + } else { + Ok(ValidationResult::Pass) + } + } +} diff --git a/programs/mpl-core/src/plugins/edition.rs b/programs/mpl-core/src/plugins/edition.rs index d0d7e279..531dc960 100644 --- a/programs/mpl-core/src/plugins/edition.rs +++ b/programs/mpl-core/src/plugins/edition.rs @@ -1,10 +1,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; -use crate::state::Authority; +use crate::{plugins::approve, state::Authority}; use super::{ - approve, reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult, + reject, Plugin, PluginType, PluginValidation, PluginValidationContext, ValidationResult, }; /// The edition plugin allows the creator to set an edition number on the asset @@ -23,12 +23,11 @@ impl PluginValidation for Edition { ) -> 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::Edition - { - reject!() - } else { - Ok(ValidationResult::Pass) + match ctx.target_plugin { + Some(Plugin::Edition(_edition)) => { + reject!() + } + _ => Ok(ValidationResult::Pass), } } @@ -38,12 +37,11 @@ impl PluginValidation for Edition { ) -> Result { // This plugin cannot be removed // always reject it. - if ctx.target_plugin.is_some() - && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Edition - { - reject!() - } else { - Ok(ValidationResult::Pass) + match ctx.target_plugin { + Some(Plugin::Edition(_edition)) => { + reject!() + } + _ => Ok(ValidationResult::Pass), } } /// Validate the revoke plugin authority lifecycle action. diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index 2a3162e9..e54cbb48 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -82,6 +82,8 @@ impl PluginType { PluginType::PermanentTransferDelegate => CheckResult::CanReject, PluginType::PermanentBurnDelegate => CheckResult::CanReject, PluginType::Edition => CheckResult::CanReject, + PluginType::Autograph => CheckResult::CanReject, + PluginType::VerifiedCreators => CheckResult::CanReject, _ => CheckResult::None, } } @@ -131,6 +133,8 @@ impl PluginType { match plugin_type { PluginType::Royalties => CheckResult::CanReject, PluginType::UpdateDelegate => CheckResult::CanApprove, + PluginType::Autograph => CheckResult::CanReject, + PluginType::VerifiedCreators => CheckResult::CanReject, _ => CheckResult::None, } } @@ -237,6 +241,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_add_plugin(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_add_plugin(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_add_plugin(ctx), } } @@ -274,6 +282,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_remove_plugin(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_remove_plugin(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_remove_plugin(ctx), } } @@ -318,6 +330,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_approve_plugin_authority(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_approve_plugin_authority(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_approve_plugin_authority(ctx), } } @@ -360,6 +376,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_revoke_plugin_authority(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_revoke_plugin_authority(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_revoke_plugin_authority(ctx), } } @@ -388,6 +408,8 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_create(ctx) } + Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_create(ctx), + Plugin::Autograph(autograph) => autograph.validate_create(ctx), } } @@ -416,6 +438,8 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_update(ctx) } + Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_update(ctx), + Plugin::Autograph(autograph) => autograph.validate_update(ctx), } } @@ -457,6 +481,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_update_plugin(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_update_plugin(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_update_plugin(ctx), }?; match (&base_result, &result) { @@ -502,6 +530,8 @@ impl Plugin { 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), } } @@ -530,6 +560,8 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_transfer(ctx) } + Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_transfer(ctx), + Plugin::Autograph(autograph) => autograph.validate_transfer(ctx), } } @@ -558,6 +590,8 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_compress(ctx) } + Plugin::VerifiedCreators(verified_creators) => verified_creators.validate_compress(ctx), + Plugin::Autograph(autograph) => autograph.validate_compress(ctx), } } @@ -588,6 +622,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_decompress(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_decompress(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_decompress(ctx), } } @@ -626,6 +664,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_add_external_plugin_adapter(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_add_external_plugin_adapter(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_add_external_plugin_adapter(ctx), } } @@ -666,6 +708,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_remove_external_plugin_adapter(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_remove_external_plugin_adapter(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_remove_external_plugin_adapter(ctx), } } @@ -717,6 +763,10 @@ impl Plugin { Plugin::ImmutableMetadata(immutable_metadata) => { immutable_metadata.validate_update_external_plugin_adapter(ctx) } + Plugin::VerifiedCreators(verified_creators) => { + verified_creators.validate_update_external_plugin_adapter(ctx) + } + Plugin::Autograph(autograph) => autograph.validate_update_external_plugin_adapter(ctx), }?; match (&base_result, &result) { @@ -812,21 +862,22 @@ pub(crate) struct PluginValidationContext<'a, 'b> { pub asset_info: Option<&'a AccountInfo<'a>>, /// The collection account. pub collection_info: Option<&'a AccountInfo<'a>>, - /// The authority. + /// The authority of the current (self) plugin pub self_authority: &'b Authority, - /// The authority account. + /// The authority account info of ix `authority` signer pub authority_info: &'a AccountInfo<'a>, - /// The resolved authority. + /// The authorities types which match the authority signer pub resolved_authorities: Option<&'b [Authority]>, - /// The new owner account. + /// The new owner account for transfers pub new_owner: Option<&'a AccountInfo<'a>>, - /// The new plugin. + /// The plugin being acted upon with new data from the ix if any. This None for create. pub target_plugin: Option<&'b Plugin>, } /// Plugin validation trait which is implemented by each plugin. pub(crate) trait PluginValidation { /// Validate the add plugin lifecycle action. + /// This gets called on all existing plugins when a new plugin is added. fn validate_add_plugin( &self, _ctx: &PluginValidationContext, @@ -835,6 +886,7 @@ pub(crate) trait PluginValidation { } /// Validate the remove plugin lifecycle action. + /// This gets called on all existing plugins when the target plugin is removed. fn validate_remove_plugin( &self, _ctx: &PluginValidationContext, @@ -843,6 +895,7 @@ pub(crate) trait PluginValidation { } /// Validate the add plugin lifecycle action. + /// This gets called on all existing plugins when a new external plugin is added. fn validate_add_external_plugin_adapter( &self, _ctx: &PluginValidationContext, @@ -851,6 +904,7 @@ pub(crate) trait PluginValidation { } /// Validate the remove plugin lifecycle action. + /// This gets called on all existing plugins when a new external plugin is removed. fn validate_remove_external_plugin_adapter( &self, _ctx: &PluginValidationContext, @@ -875,6 +929,7 @@ pub(crate) trait PluginValidation { } /// Validate the create lifecycle action. + /// This ONLY gets called to validate the self plugin fn validate_create( &self, _ctx: &PluginValidationContext, @@ -883,6 +938,7 @@ pub(crate) trait PluginValidation { } /// Validate the update lifecycle action. + /// This gets called on all existing plugins when an asset or collection is updated. fn validate_update( &self, _ctx: &PluginValidationContext, @@ -891,6 +947,7 @@ pub(crate) trait PluginValidation { } /// Validate the update_plugin lifecycle action. + /// This gets called on all existing plugins when a plugin is updated. fn validate_update_plugin( &self, _ctx: &PluginValidationContext, @@ -899,6 +956,7 @@ pub(crate) trait PluginValidation { } /// Validate the burn lifecycle action. + /// This gets called on all existing plugins when an asset is burned. fn validate_burn( &self, _ctx: &PluginValidationContext, @@ -907,6 +965,7 @@ pub(crate) trait PluginValidation { } /// Validate the transfer lifecycle action. + /// This gets called on all existing plugins when an asset is transferred. fn validate_transfer( &self, _ctx: &PluginValidationContext, diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs index 93ef8da7..109987cc 100644 --- a/programs/mpl-core/src/plugins/mod.rs +++ b/programs/mpl-core/src/plugins/mod.rs @@ -13,6 +13,7 @@ mod oracle; mod master_edition; +mod autograph; mod permanent_burn_delegate; mod permanent_freeze_delegate; mod permanent_transfer_delegate; @@ -22,9 +23,11 @@ mod royalties; mod transfer; mod update_delegate; mod utils; +mod verified_creators; pub use add_blocker::*; pub use attributes::*; +pub use autograph::*; pub use burn_delegate::*; pub use data_store::*; pub use edition::*; @@ -44,6 +47,7 @@ pub use royalties::*; pub use transfer::*; pub use update_delegate::*; pub use utils::*; +pub use verified_creators::*; use borsh::{BorshDeserialize, BorshSerialize}; use num_derive::ToPrimitive; @@ -87,6 +91,10 @@ pub enum Plugin { AddBlocker(AddBlocker), /// ImmutableMetadata plugin. Makes metadata of the asset immutable. ImmutableMetadata(ImmutableMetadata), + /// VerifiedCreators plugin allows update auth to specify verified creators and additional creators to sign + VerifiedCreators(VerifiedCreators), + /// Autograph plugin allows anybody to add their signature to the asset with an optional message + Autograph(Autograph), } impl Plugin { @@ -157,6 +165,10 @@ pub enum PluginType { AddBlocker, /// ImmutableMetadata plugin. ImmutableMetadata, + /// VerifiedCreators plugin. + VerifiedCreators, + /// Autograph plugin. + Autograph, } impl DataBlob for PluginType { @@ -185,6 +197,8 @@ impl From<&Plugin> for PluginType { Plugin::PermanentBurnDelegate(_) => PluginType::PermanentBurnDelegate, Plugin::Edition(_) => PluginType::Edition, Plugin::MasterEdition(_) => PluginType::MasterEdition, + Plugin::VerifiedCreators(_) => PluginType::VerifiedCreators, + Plugin::Autograph(_) => PluginType::Autograph, } } } @@ -206,6 +220,8 @@ impl PluginType { PluginType::PermanentBurnDelegate => Authority::UpdateAuthority, PluginType::Edition => Authority::UpdateAuthority, PluginType::MasterEdition => Authority::UpdateAuthority, + PluginType::VerifiedCreators => Authority::UpdateAuthority, + PluginType::Autograph => Authority::Owner, } } } diff --git a/programs/mpl-core/src/plugins/royalties.rs b/programs/mpl-core/src/plugins/royalties.rs index cc8e208d..4cb5626b 100644 --- a/programs/mpl-core/src/plugins/royalties.rs +++ b/programs/mpl-core/src/plugins/royalties.rs @@ -102,9 +102,12 @@ impl PluginValidation for Royalties { fn validate_add_plugin( &self, - _ctx: &PluginValidationContext, + ctx: &PluginValidationContext, ) -> Result { - validate_royalties(self) + match ctx.target_plugin { + Some(Plugin::Royalties(_royalties)) => validate_royalties(self), + _ => Ok(ValidationResult::Pass), + } } fn validate_update_plugin( diff --git a/programs/mpl-core/src/plugins/verified_creators.rs b/programs/mpl-core/src/plugins/verified_creators.rs new file mode 100644 index 00000000..38b2fe13 --- /dev/null +++ b/programs/mpl-core/src/plugins/verified_creators.rs @@ -0,0 +1,218 @@ +use std::collections::{BTreeMap, HashSet}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::{program_error::ProgramError, pubkey::Pubkey}; + +use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; + +use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; + +/// The creator on an asset and whether or not they are verified. +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] +pub struct VerifiedCreatorsSignature { + address: Pubkey, + verified: bool, +} + +/// Structure for storing verified creators, often used in conjunction with the Royalties plugin +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Eq, PartialEq)] +pub struct VerifiedCreators { + /// A list of signatures + signatures: Vec, +} + +struct SignatureChangeIndices { + /// Indices of added signatures on new_verified_creators + added: Vec, + /// Indices of changed signatures on new_verified_creators + changed: Vec, + /// Indices of removed signatures on verified_creators + removed: Vec, +} + +fn calculate_signature_changes( + new_verified_creators: &VerifiedCreators, + verified_creators: Option<&VerifiedCreators>, +) -> Result { + let existing_map = verified_creators.map_or_else(BTreeMap::new, |verified_creators| { + verified_creators + .signatures + .iter() + .map(|sig| (sig.address, sig)) + .collect::>() + }); + + let new_signatures: HashSet<_> = new_verified_creators + .signatures + .iter() + .map(|sig| sig.address) + .collect(); + + if new_verified_creators.signatures.len() != new_signatures.len() { + // Ensure there are no duplicate signatures + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::InvalidPluginSetting.into()); + } + + let mut result = SignatureChangeIndices { + added: Vec::new(), + changed: Vec::new(), + removed: Vec::new(), + }; + + for (i, sig) in new_verified_creators.signatures.iter().enumerate() { + match existing_map.get(&sig.address) { + Some(existing_sig) => { + if existing_sig.verified != sig.verified { + result.changed.push(i as u8); + } + } + None => { + result.added.push(i as u8); + } + } + } + + if let Some(verified_creators) = verified_creators { + for (i, sig) in verified_creators.signatures.iter().enumerate() { + if !new_signatures.contains(&sig.address) { + result.removed.push(i as u8); + } + } + } + + Ok(result) +} + +fn validate_verified_creators_as_creator( + new_verified_creators: &VerifiedCreators, + verified_creators: &VerifiedCreators, + authority: &Pubkey, +) -> Result { + // Track any changes in verification status + let changes = calculate_signature_changes(new_verified_creators, Some(verified_creators))?; + + if !changes.added.is_empty() || !changes.removed.is_empty() { + // creators cannot add new allowable signatures or remove existing ones + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::MissingSigner.into()); + } + + for change in changes.changed.iter() { + let sig = &new_verified_creators.signatures[*change as usize]; + if &sig.address != authority { + // creators may only change their own verified status + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::MissingSigner.into()); + } + } + + Ok(ValidationResult::Pass) +} + +fn validate_verified_creators_as_plugin_authority( + new_verified_creators: &VerifiedCreators, + verified_creators: Option<&VerifiedCreators>, + authority: &Pubkey, +) -> Result { + // The plugin auth is allowed to: add/remove unverified creators, add self and sign for self. + // The plugin auth cannot remove or unverify any existing creators other than self. + // This is in line with legacy Token Metadata behaviour for verified creators + + let changes = calculate_signature_changes(new_verified_creators, verified_creators)?; + + for removal in changes.removed.iter() { + let sig = &verified_creators.unwrap().signatures[*removal as usize]; + if sig.verified && &sig.address != authority { + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::InvalidPluginOperation.into()); + } + } + + for change in changes.changed.iter() { + let sig = &new_verified_creators.signatures[*change as usize]; + if &sig.address != authority { + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::InvalidPluginOperation.into()); + } + } + + for addition in changes.added.iter() { + let sig = &new_verified_creators.signatures[*addition as usize]; + if sig.verified && &sig.address != authority { + solana_program::msg!("Verified creators: Rejected"); + return Err(MplCoreError::MissingSigner.into()); + } + } + + Ok(ValidationResult::Pass) +} + +impl PluginValidation for VerifiedCreators { + fn validate_create( + &self, + ctx: &PluginValidationContext, + ) -> Result { + validate_verified_creators_as_plugin_authority(self, None, ctx.authority_info.key) + } + + fn validate_add_plugin( + &self, + ctx: &PluginValidationContext, + ) -> Result { + match ctx.target_plugin { + Some(Plugin::VerifiedCreators(_verified_creators)) => { + validate_verified_creators_as_plugin_authority(self, None, ctx.authority_info.key) + } + _ => Ok(ValidationResult::Pass), + } + } + + fn validate_update_plugin( + &self, + ctx: &PluginValidationContext, + ) -> 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) + } + } + _ => Ok(ValidationResult::Pass), + } + } + + /// Validate the revoke plugin authority lifecycle action. + fn validate_revoke_plugin_authority( + &self, + ctx: &PluginValidationContext, + ) -> Result { + if ctx.self_authority + == &(Authority::Address { + address: *ctx.authority_info.key, + }) + && ctx.target_plugin.is_some() + && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::VerifiedCreators + { + solana_program::msg!("Verified creators: Approved"); + Ok(ValidationResult::Approved) + } else { + Ok(ValidationResult::Pass) + } + } +} diff --git a/programs/mpl-core/src/processor/add_plugin.rs b/programs/mpl-core/src/processor/add_plugin.rs index e2b75c96..629de7d3 100644 --- a/programs/mpl-core/src/processor/add_plugin.rs +++ b/programs/mpl-core/src/processor/add_plugin.rs @@ -53,7 +53,8 @@ pub(crate) fn add_plugin<'a>( return Err(MplCoreError::InvalidPlugin.into()); } - //TODO: Seed with Rejected + // TODO: Seed with Rejected + // TODO: refactor to allow add_plugin to approve additions let validation_ctx = PluginValidationContext { accounts, asset_info: Some(ctx.accounts.asset), From 85d28b7171ecb45c4302d30f6bdc02d86f8cbc6e Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Wed, 5 Jun 2024 14:54:52 -0400 Subject: [PATCH 3/3] Adding abstain macro. --- programs/mpl-core/src/plugins/add_blocker.rs | 7 ++- programs/mpl-core/src/plugins/autograph.rs | 20 ++++---- .../mpl-core/src/plugins/burn_delegate.rs | 6 +-- programs/mpl-core/src/plugins/data_store.rs | 6 ++- programs/mpl-core/src/plugins/edition.rs | 9 ++-- .../mpl-core/src/plugins/freeze_delegate.rs | 14 +++--- programs/mpl-core/src/plugins/lifecycle.rs | 46 +++++++++++-------- .../mpl-core/src/plugins/lifecycle_hook.rs | 6 +-- programs/mpl-core/src/plugins/oracle.rs | 6 +-- .../src/plugins/permanent_burn_delegate.rs | 6 +-- .../src/plugins/permanent_freeze_delegate.rs | 12 ++--- .../plugins/permanent_transfer_delegate.rs | 6 +-- programs/mpl-core/src/plugins/royalties.rs | 20 ++++---- programs/mpl-core/src/plugins/transfer.rs | 8 ++-- .../mpl-core/src/plugins/update_delegate.rs | 16 ++++--- .../mpl-core/src/plugins/verified_creators.rs | 12 ++--- programs/mpl-core/src/state/asset.rs | 32 ++++++------- programs/mpl-core/src/state/collection.rs | 28 +++++------ .../mpl-core/src/state/update_authority.rs | 17 +++---- 19 files changed, 149 insertions(+), 128 deletions(-) diff --git a/programs/mpl-core/src/plugins/add_blocker.rs b/programs/mpl-core/src/plugins/add_blocker.rs index 4e70ae12..f2ab69de 100644 --- a/programs/mpl-core/src/plugins/add_blocker.rs +++ b/programs/mpl-core/src/plugins/add_blocker.rs @@ -1,7 +1,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; -use crate::state::{Authority, DataBlob}; +use crate::{ + plugins::abstain, + state::{Authority, DataBlob}, +}; use super::{reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult}; @@ -31,7 +34,7 @@ impl PluginValidation for AddBlocker { if plugin.manager() == Authority::Owner || PluginType::from(plugin) == PluginType::AddBlocker { - return Ok(ValidationResult::Pass); + return abstain!(); } } diff --git a/programs/mpl-core/src/plugins/autograph.rs b/programs/mpl-core/src/plugins/autograph.rs index d0b63a1e..673098d1 100644 --- a/programs/mpl-core/src/plugins/autograph.rs +++ b/programs/mpl-core/src/plugins/autograph.rs @@ -5,7 +5,9 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; -use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + abstain, approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult, +}; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] @@ -74,7 +76,7 @@ fn validate_autograph( } } - Ok(ValidationResult::Pass) + abstain!() } impl PluginValidation for Autograph { @@ -92,9 +94,9 @@ impl PluginValidation for Autograph { match ctx.target_plugin { Some(Plugin::Autograph(_autograph)) => { validate_autograph(self, None, ctx.authority_info.key, true)?; - Ok(ValidationResult::Approved) + approve!() } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -113,10 +115,9 @@ impl PluginValidation for Autograph { ctx.authority_info.key, resolved_authorities.contains(ctx.self_authority), )?; - solana_program::msg!("Autograph: Approved"); - Ok(ValidationResult::Approved) + approve!() } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -132,10 +133,9 @@ impl PluginValidation for Autograph { && ctx.target_plugin.is_some() && PluginType::from(ctx.target_plugin.unwrap()) == PluginType::Autograph { - solana_program::msg!("Autograph: Approved"); - Ok(ValidationResult::Approved) + approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/burn_delegate.rs b/programs/mpl-core/src/plugins/burn_delegate.rs index c4326586..5d62f615 100644 --- a/programs/mpl-core/src/plugins/burn_delegate.rs +++ b/programs/mpl-core/src/plugins/burn_delegate.rs @@ -2,7 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; use crate::{ - plugins::{approve, PluginType}, + plugins::{abstain, approve, PluginType}, state::{Authority, DataBlob}, }; @@ -49,7 +49,7 @@ impl PluginValidation for BurnDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -67,7 +67,7 @@ impl PluginValidation for BurnDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/data_store.rs b/programs/mpl-core/src/plugins/data_store.rs index 76e959d7..da45859e 100644 --- a/programs/mpl-core/src/plugins/data_store.rs +++ b/programs/mpl-core/src/plugins/data_store.rs @@ -1,6 +1,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::program_error::ProgramError; +use crate::plugins::abstain; + use super::{ Authority, ExternalPluginAdapterSchema, PluginValidation, PluginValidationContext, ValidationResult, @@ -34,14 +36,14 @@ impl PluginValidation for DataStore { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } fn validate_transfer( &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/edition.rs b/programs/mpl-core/src/plugins/edition.rs index 531dc960..11e6afb2 100644 --- a/programs/mpl-core/src/plugins/edition.rs +++ b/programs/mpl-core/src/plugins/edition.rs @@ -4,7 +4,8 @@ use solana_program::program_error::ProgramError; use crate::{plugins::approve, state::Authority}; use super::{ - reject, Plugin, PluginType, PluginValidation, PluginValidationContext, ValidationResult, + abstain, reject, Plugin, PluginType, PluginValidation, PluginValidationContext, + ValidationResult, }; /// The edition plugin allows the creator to set an edition number on the asset @@ -27,7 +28,7 @@ impl PluginValidation for Edition { Some(Plugin::Edition(_edition)) => { reject!() } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -41,7 +42,7 @@ impl PluginValidation for Edition { Some(Plugin::Edition(_edition)) => { reject!() } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } /// Validate the revoke plugin authority lifecycle action. @@ -58,7 +59,7 @@ impl PluginValidation for Edition { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/freeze_delegate.rs b/programs/mpl-core/src/plugins/freeze_delegate.rs index a40c7193..160a5c89 100644 --- a/programs/mpl-core/src/plugins/freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/freeze_delegate.rs @@ -3,7 +3,9 @@ use solana_program::program_error::ProgramError; use crate::state::{Authority, DataBlob}; -use super::{approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + abstain, approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, +}; /// The freeze delegate plugin allows any authority to lock the asset so it's no longer transferable. /// The default authority for this plugin is the owner. @@ -45,7 +47,7 @@ impl PluginValidation for FreezeDelegate { if self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -56,7 +58,7 @@ impl PluginValidation for FreezeDelegate { if self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -69,7 +71,7 @@ impl PluginValidation for FreezeDelegate { return reject!(); } } - Ok(ValidationResult::Pass) + abstain!() } /// Validate the revoke plugin authority lifecycle action. @@ -89,7 +91,7 @@ impl PluginValidation for FreezeDelegate { } } - Ok(ValidationResult::Pass) + abstain!() } /// Validate the remove plugin lifecycle action. @@ -100,7 +102,7 @@ impl PluginValidation for FreezeDelegate { if ctx.target_plugin.is_some() && self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/lifecycle.rs b/programs/mpl-core/src/plugins/lifecycle.rs index e54cbb48..b6679591 100644 --- a/programs/mpl-core/src/plugins/lifecycle.rs +++ b/programs/mpl-core/src/plugins/lifecycle.rs @@ -804,6 +804,14 @@ pub enum ValidationResult { ForceApproved, } +// Create a shortcut macro for passing on a lifecycle action. +macro_rules! abstain { + () => {{ + Ok(ValidationResult::Pass) + }}; +} +pub(crate) use abstain; + // Create a shortcut macro for rejecting a lifecycle action. macro_rules! reject { () => {{ @@ -822,7 +830,7 @@ macro_rules! approve { } pub(crate) use approve; -// Create a shortcut macro for approving a lifecycle action. +// Create a shortcut macro for force-approving a lifecycle action. macro_rules! force_approve { () => {{ solana_program::msg!("{}:{}:ForceApprove", std::file!(), std::line!()); @@ -882,7 +890,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the remove plugin lifecycle action. @@ -891,7 +899,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the add plugin lifecycle action. @@ -900,7 +908,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the remove plugin lifecycle action. @@ -909,7 +917,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the approve plugin authority lifecycle action. @@ -917,7 +925,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the revoke plugin authority lifecycle action. @@ -925,7 +933,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the create lifecycle action. @@ -934,7 +942,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the update lifecycle action. @@ -943,7 +951,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the update_plugin lifecycle action. @@ -952,7 +960,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the burn lifecycle action. @@ -961,7 +969,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the transfer lifecycle action. @@ -970,7 +978,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the compress lifecycle action. @@ -978,7 +986,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the decompress lifecycle action. @@ -986,7 +994,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the add_authority lifecycle action. @@ -994,7 +1002,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the add_authority lifecycle action. @@ -1002,7 +1010,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the update_plugin lifecycle action. @@ -1010,7 +1018,7 @@ pub(crate) trait PluginValidation { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } } @@ -1077,7 +1085,7 @@ pub(crate) fn validate_plugin_checks<'a>( } else if approved { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -1152,6 +1160,6 @@ pub(crate) fn validate_external_plugin_adapter_checks<'a>( if approved { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/lifecycle_hook.rs b/programs/mpl-core/src/plugins/lifecycle_hook.rs index 81938d5c..2c593de9 100644 --- a/programs/mpl-core/src/plugins/lifecycle_hook.rs +++ b/programs/mpl-core/src/plugins/lifecycle_hook.rs @@ -2,7 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use super::{ - Authority, ExternalCheckResult, ExternalPluginAdapterSchema, ExtraAccount, + abstain, Authority, ExternalCheckResult, ExternalPluginAdapterSchema, ExtraAccount, HookableLifecycleEvent, PluginValidation, PluginValidationContext, ValidationResult, }; @@ -43,14 +43,14 @@ impl PluginValidation for LifecycleHook { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } fn validate_transfer( &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/oracle.rs b/programs/mpl-core/src/plugins/oracle.rs index a0a0c1c7..d5882d4c 100644 --- a/programs/mpl-core/src/plugins/oracle.rs +++ b/programs/mpl-core/src/plugins/oracle.rs @@ -4,8 +4,8 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::error::MplCoreError; use super::{ - Authority, ExternalCheckResult, ExternalValidationResult, ExtraAccount, HookableLifecycleEvent, - PluginValidation, PluginValidationContext, ValidationResult, + abstain, Authority, ExternalCheckResult, ExternalValidationResult, ExtraAccount, + HookableLifecycleEvent, PluginValidation, PluginValidationContext, ValidationResult, }; /// Oracle plugin that allows getting a `ValidationResult` for a lifecycle event from an arbitrary @@ -41,7 +41,7 @@ impl PluginValidation for Oracle { &self, _ctx: &PluginValidationContext, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } fn validate_create( diff --git a/programs/mpl-core/src/plugins/permanent_burn_delegate.rs b/programs/mpl-core/src/plugins/permanent_burn_delegate.rs index dc40fb37..348b9f07 100644 --- a/programs/mpl-core/src/plugins/permanent_burn_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_burn_delegate.rs @@ -4,7 +4,7 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; use super::{ - approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, + abstain, approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult, }; @@ -36,7 +36,7 @@ impl PluginValidation for PermanentBurnDelegate { { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -57,6 +57,6 @@ impl PluginValidation for PermanentBurnDelegate { } } - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs b/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs index eae82798..432482c8 100644 --- a/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_freeze_delegate.rs @@ -6,7 +6,7 @@ use crate::{ state::{Authority, DataBlob}, }; -use super::{approve, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{abstain, approve, PluginValidation, PluginValidationContext, ValidationResult}; /// The permanent freeze plugin allows any authority to lock the asset so it's no longer transferable. /// The default authority for this plugin is the update authority. @@ -48,7 +48,7 @@ impl PluginValidation for PermanentFreezeDelegate { if self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -59,7 +59,7 @@ impl PluginValidation for PermanentFreezeDelegate { if self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -74,7 +74,7 @@ impl PluginValidation for PermanentFreezeDelegate { { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -92,7 +92,7 @@ impl PluginValidation for PermanentFreezeDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -104,7 +104,7 @@ impl PluginValidation for PermanentFreezeDelegate { if ctx.target_plugin.is_some() && self.frozen { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs b/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs index 9d94551f..f7382033 100644 --- a/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs +++ b/programs/mpl-core/src/plugins/permanent_transfer_delegate.rs @@ -4,7 +4,7 @@ use solana_program::program_error::ProgramError; use crate::state::DataBlob; use super::{ - approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, + abstain, approve, force_approve, reject, PluginType, PluginValidation, PluginValidationContext, ValidationResult, }; @@ -36,7 +36,7 @@ impl PluginValidation for PermanentTransferDelegate { { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -57,6 +57,6 @@ impl PluginValidation for PermanentTransferDelegate { } } - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/royalties.rs b/programs/mpl-core/src/plugins/royalties.rs index 4cb5626b..ec93acb5 100644 --- a/programs/mpl-core/src/plugins/royalties.rs +++ b/programs/mpl-core/src/plugins/royalties.rs @@ -5,7 +5,9 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; -use super::{approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + abstain, approve, reject, Plugin, PluginValidation, PluginValidationContext, ValidationResult, +}; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] @@ -61,7 +63,7 @@ fn validate_royalties(royalties: &Royalties) -> Result Result { let new_owner = ctx.new_owner.ok_or(MplCoreError::MissingNewOwner)?; match &self.rule_set { - RuleSet::None => Ok(ValidationResult::Pass), + RuleSet::None => abstain!(), RuleSet::ProgramAllowList(allow_list) => { if allow_list.contains(ctx.authority_info.owner) && allow_list.contains(new_owner.owner) { - Ok(ValidationResult::Pass) + abstain!() } else { reject!() } @@ -94,7 +96,7 @@ impl PluginValidation for Royalties { { reject!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } @@ -106,7 +108,7 @@ impl PluginValidation for Royalties { ) -> Result { match ctx.target_plugin { Some(Plugin::Royalties(_royalties)) => validate_royalties(self), - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -124,10 +126,10 @@ impl PluginValidation for Royalties { if resolved_authorities.contains(ctx.self_authority) { validate_royalties(royalties) } else { - Ok(ValidationResult::Pass) + abstain!() } } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -145,7 +147,7 @@ impl PluginValidation for Royalties { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/transfer.rs b/programs/mpl-core/src/plugins/transfer.rs index 548a7fda..b91499e1 100644 --- a/programs/mpl-core/src/plugins/transfer.rs +++ b/programs/mpl-core/src/plugins/transfer.rs @@ -6,7 +6,7 @@ use crate::{ state::{Authority, DataBlob}, }; -use super::{approve, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{abstain, approve, PluginValidation, PluginValidationContext, ValidationResult}; /// This plugin manages the ability to transfer an asset and any authorities /// approved are permitted to transfer the asset on behalf of the owner. @@ -49,7 +49,7 @@ impl PluginValidation for TransferDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -64,7 +64,7 @@ impl PluginValidation for TransferDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -82,7 +82,7 @@ impl PluginValidation for TransferDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/plugins/update_delegate.rs b/programs/mpl-core/src/plugins/update_delegate.rs index 3dd1c23c..7637f1a6 100644 --- a/programs/mpl-core/src/plugins/update_delegate.rs +++ b/programs/mpl-core/src/plugins/update_delegate.rs @@ -7,7 +7,9 @@ use crate::{ state::{Authority, DataBlob}, }; -use super::{approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{ + abstain, approve, Plugin, PluginValidation, PluginValidationContext, ValidationResult, +}; /// This plugin manages additional permissions to burn. /// Any authorities approved are given permission to burn the asset on behalf of the owner. @@ -51,7 +53,7 @@ impl PluginValidation for UpdateDelegate { if !self.additional_delegates.is_empty() { return Err(MplCoreError::NotAvailable.into()); } - Ok(ValidationResult::Pass) + abstain!() } fn validate_add_plugin( @@ -73,7 +75,7 @@ impl PluginValidation for UpdateDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } else { Err(MplCoreError::InvalidPlugin.into()) @@ -93,7 +95,7 @@ impl PluginValidation for UpdateDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } else { Err(MplCoreError::InvalidPlugin.into()) @@ -114,7 +116,7 @@ impl PluginValidation for UpdateDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -129,7 +131,7 @@ impl PluginValidation for UpdateDelegate { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -144,6 +146,6 @@ impl PluginValidation for UpdateDelegate { } } - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/plugins/verified_creators.rs b/programs/mpl-core/src/plugins/verified_creators.rs index 38b2fe13..66719fa8 100644 --- a/programs/mpl-core/src/plugins/verified_creators.rs +++ b/programs/mpl-core/src/plugins/verified_creators.rs @@ -5,7 +5,7 @@ use solana_program::{program_error::ProgramError, pubkey::Pubkey}; use crate::{error::MplCoreError, plugins::PluginType, state::Authority}; -use super::{Plugin, PluginValidation, PluginValidationContext, ValidationResult}; +use super::{abstain, Plugin, PluginValidation, PluginValidationContext, ValidationResult}; /// The creator on an asset and whether or not they are verified. #[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Hash)] @@ -107,7 +107,7 @@ fn validate_verified_creators_as_creator( } } - Ok(ValidationResult::Pass) + abstain!() } fn validate_verified_creators_as_plugin_authority( @@ -145,7 +145,7 @@ fn validate_verified_creators_as_plugin_authority( } } - Ok(ValidationResult::Pass) + abstain!() } impl PluginValidation for VerifiedCreators { @@ -164,7 +164,7 @@ impl PluginValidation for VerifiedCreators { Some(Plugin::VerifiedCreators(_verified_creators)) => { validate_verified_creators_as_plugin_authority(self, None, ctx.authority_info.key) } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -193,7 +193,7 @@ impl PluginValidation for VerifiedCreators { Ok(ValidationResult::Approved) } } - _ => Ok(ValidationResult::Pass), + _ => abstain!(), } } @@ -212,7 +212,7 @@ impl PluginValidation for VerifiedCreators { solana_program::msg!("Verified creators: Approved"); Ok(ValidationResult::Approved) } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/state/asset.rs b/programs/mpl-core/src/state/asset.rs index 26f91585..44655511 100644 --- a/programs/mpl-core/src/state/asset.rs +++ b/programs/mpl-core/src/state/asset.rs @@ -8,7 +8,7 @@ use std::mem::size_of; use crate::{ error::MplCoreError, - plugins::{approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{abstain, approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, state::{Compressible, CompressionProof, DataBlob, Key, SolanaAccount}, }; @@ -148,7 +148,7 @@ impl AssetV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -170,7 +170,7 @@ impl AssetV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -181,7 +181,7 @@ impl AssetV1 { _plugin: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the approve plugin authority lifecycle event. @@ -198,10 +198,10 @@ impl AssetV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -219,10 +219,10 @@ impl AssetV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -236,7 +236,7 @@ impl AssetV1 { if authority_info.key == &self.update_authority.key() { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -250,7 +250,7 @@ impl AssetV1 { if authority_info.key == &self.owner { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -264,7 +264,7 @@ impl AssetV1 { if authority_info.key == &self.owner { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -278,7 +278,7 @@ impl AssetV1 { if authority_info.key == &self.owner { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -292,7 +292,7 @@ impl AssetV1 { if authority_info.key == &self.owner { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -307,7 +307,7 @@ impl AssetV1 { if UpdateAuthority::Address(*authority_info.key) == self.update_authority { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -321,7 +321,7 @@ impl AssetV1 { if self.update_authority == UpdateAuthority::Address(*authority_info.key) { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -335,7 +335,7 @@ impl AssetV1 { if self.update_authority == UpdateAuthority::Address(*authority_info.key) { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } } diff --git a/programs/mpl-core/src/state/collection.rs b/programs/mpl-core/src/state/collection.rs index ccd92a4c..1b4767fd 100644 --- a/programs/mpl-core/src/state/collection.rs +++ b/programs/mpl-core/src/state/collection.rs @@ -4,7 +4,7 @@ use solana_program::{account_info::AccountInfo, program_error::ProgramError, pub use crate::{ error::MplCoreError, - plugins::{approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, + plugins::{abstain, approve, CheckResult, ExternalPluginAdapter, Plugin, ValidationResult}, }; use super::{Authority, CoreAsset, DataBlob, Key, SolanaAccount, UpdateAuthority}; @@ -130,7 +130,7 @@ impl CollectionV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -151,7 +151,7 @@ impl CollectionV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -162,7 +162,7 @@ impl CollectionV1 { _plugin: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the approve plugin authority lifecycle event. @@ -182,7 +182,7 @@ impl CollectionV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -203,7 +203,7 @@ impl CollectionV1 { { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -214,7 +214,7 @@ impl CollectionV1 { _: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the burn lifecycle event. @@ -224,7 +224,7 @@ impl CollectionV1 { _: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the update lifecycle event. @@ -237,7 +237,7 @@ impl CollectionV1 { if authority_info.key == &self.update_authority { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -248,7 +248,7 @@ impl CollectionV1 { _: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the decompress lifecycle event. @@ -258,7 +258,7 @@ impl CollectionV1 { _: Option<&Plugin>, _: Option<&ExternalPluginAdapter>, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the add external plugin adapter lifecycle event. @@ -272,7 +272,7 @@ impl CollectionV1 { if *authority_info.key == self.update_authority { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -286,7 +286,7 @@ impl CollectionV1 { if self.update_authority == *authority_info.key { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } @@ -300,7 +300,7 @@ impl CollectionV1 { if self.update_authority == *authority_info.key { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } diff --git a/programs/mpl-core/src/state/update_authority.rs b/programs/mpl-core/src/state/update_authority.rs index 612a0542..b1acd03f 100644 --- a/programs/mpl-core/src/state/update_authority.rs +++ b/programs/mpl-core/src/state/update_authority.rs @@ -9,7 +9,8 @@ use crate::{ TransferV1Accounts, UpdateV1Accounts, }, plugins::{ - approve, fetch_plugin, reject, CheckResult, PluginType, UpdateDelegate, ValidationResult, + abstain, approve, fetch_plugin, reject, CheckResult, PluginType, UpdateDelegate, + ValidationResult, }, processor::CreateV2Args, state::{Authority, CollectionV1, SolanaAccount}, @@ -91,10 +92,10 @@ impl UpdateAuthority { return reject!(); } - Ok(ValidationResult::Pass) + abstain!() } // If you're not trying add a collection, then just pass. - (_, UpdateAuthority::Address(_)) => Ok(ValidationResult::Pass), + (_, UpdateAuthority::Address(_)) => abstain!(), // Otherwise reject because you're doing something weird. _ => reject!(), } @@ -114,13 +115,13 @@ impl UpdateAuthority { if ctx.authority.unwrap_or(ctx.payer).key == authority { approve!() } else { - Ok(ValidationResult::Pass) + abstain!() } } /// Validate the burn lifecycle event. pub fn validate_burn(&self, _ctx: &BurnV1Accounts) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the transfer lifecycle event. @@ -128,7 +129,7 @@ impl UpdateAuthority { &self, _ctx: &TransferV1Accounts, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the compress lifecycle event. @@ -136,7 +137,7 @@ impl UpdateAuthority { &self, _ctx: &CompressV1Accounts, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } /// Validate the decompress lifecycle event. @@ -144,6 +145,6 @@ impl UpdateAuthority { &self, _ctx: &DecompressV1Accounts, ) -> Result { - Ok(ValidationResult::Pass) + abstain!() } }