Skip to content

Commit

Permalink
Merge pull request #11 from metaplex-foundation/danenbm/common-valida…
Browse files Browse the repository at this point in the history
…tion-fns

Common validation functions
  • Loading branch information
danenbm authored Mar 8, 2024
2 parents 6a41cec + 75a0e9b commit 5f21cd4
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 330 deletions.
13 changes: 13 additions & 0 deletions clients/js/src/generated/errors/mplCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@ export class MissingUpdateAuthorityError extends ProgramError {
codeToErrorMap.set(0x14, MissingUpdateAuthorityError);
nameToErrorMap.set('MissingUpdateAuthority', MissingUpdateAuthorityError);

/** MissingNewOwner: Missing new owner */
export class MissingNewOwnerError extends ProgramError {
override readonly name: string = 'MissingNewOwner';

readonly code: number = 0x15; // 21

constructor(program: Program, cause?: Error) {
super('Missing new owner', program, cause);
}
}
codeToErrorMap.set(0x15, MissingNewOwnerError);
nameToErrorMap.set('MissingNewOwner', MissingNewOwnerError);

/**
* Attempts to resolve a custom program error from the provided error code.
* @category Errors
Expand Down
3 changes: 3 additions & 0 deletions clients/rust/src/generated/errors/mpl_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ pub enum MplCoreError {
/// 20 (0x14) - Missing update authority
#[error("Missing update authority")]
MissingUpdateAuthority,
/// 21 (0x15) - Missing new owner
#[error("Missing new owner")]
MissingNewOwner,
}

impl solana_program::program_error::PrintProgramError for MplCoreError {
Expand Down
5 changes: 5 additions & 0 deletions idls/mpl_core.json
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,11 @@
"code": 20,
"name": "MissingUpdateAuthority",
"msg": "Missing update authority"
},
{
"code": 21,
"name": "MissingNewOwner",
"msg": "Missing new owner"
}
],
"metadata": {
Expand Down
4 changes: 4 additions & 0 deletions programs/mpl-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ pub enum MplCoreError {
/// 20 - Missing update authority
#[error("Missing update authority")]
MissingUpdateAuthority,

/// 20 - Missing new owner
#[error("Missing new owner")]
MissingNewOwner,
}

impl PrintProgramError for MplCoreError {
Expand Down
25 changes: 16 additions & 9 deletions programs/mpl-core/src/plugins/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl Plugin {
pub(crate) fn validate_update(
plugin: &Plugin,
authority: &AccountInfo,
_unused: Option<&AccountInfo>,
authorities: &Authority,
) -> Result<ValidationResult, ProgramError> {
match plugin {
Expand Down Expand Up @@ -145,6 +146,7 @@ impl Plugin {
pub(crate) fn validate_burn(
plugin: &Plugin,
authority: &AccountInfo,
_unused: Option<&AccountInfo>,
authorities: &Authority,
) -> Result<ValidationResult, ProgramError> {
match plugin {
Expand All @@ -160,12 +162,13 @@ impl Plugin {
}

/// Route the validation of the transfer action to the appropriate plugin.
pub(crate) fn validate_transfer<'a>(
pub(crate) fn validate_transfer(
plugin: &Plugin,
authority: &AccountInfo<'a>,
new_owner: &AccountInfo<'a>,
authority: &AccountInfo,
new_owner: Option<&AccountInfo>,
authorities: &Authority,
) -> Result<ValidationResult, ProgramError> {
let new_owner = new_owner.ok_or(MplCoreError::MissingNewOwner)?;
match plugin {
Plugin::Reserved => Err(MplCoreError::InvalidPlugin.into()),
Plugin::Royalties(royalties) => {
Expand Down Expand Up @@ -379,17 +382,20 @@ pub(crate) trait PluginValidation {
}
}

pub(crate) fn validate_plugin_checks<'a, F>(
pub(crate) fn validate_plugin_checks<'a>(
key: Key,
checks: &BTreeMap<PluginType, (Key, CheckResult, RegistryRecord)>,
authority: &AccountInfo<'a>,
new_owner: Option<&AccountInfo>,
asset: &AccountInfo<'a>,
collection: Option<&AccountInfo<'a>>,
validate_fp: F,
) -> Result<bool, ProgramError>
where
F: Fn(&Plugin, &AccountInfo<'a>, &Authority) -> Result<ValidationResult, ProgramError>,
{
validate_fp: fn(
&Plugin,
&AccountInfo<'a>,
Option<&AccountInfo>,
&Authority,
) -> Result<ValidationResult, ProgramError>,
) -> Result<bool, ProgramError> {
for (_, (check_key, check_result, registry_record)) in checks {
if *check_key == key
&& matches!(
Expand All @@ -407,6 +413,7 @@ where
let result = validate_fp(
&Plugin::load(account, registry_record.offset)?,
authority,
new_owner,
&registry_record.authority,
)?;
match result {
Expand Down
142 changes: 22 additions & 120 deletions programs/mpl-core/src/processor/burn.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use std::collections::BTreeMap;

use borsh::{BorshDeserialize, BorshSerialize};
use mpl_utils::assert_signer;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult};

use crate::{
error::MplCoreError,
instruction::accounts::{BurnAccounts, BurnCollectionAccounts},
plugins::{
validate_plugin_checks, CheckResult, Plugin, PluginType, RegistryRecord, ValidationResult,
plugins::{Plugin, PluginType},
state::{Asset, Collection, Compressible, CompressionProof, Key},
utils::{
close_program_account, load_key, validate_asset_permissions,
validate_collection_permissions, verify_proof,
},
state::{Asset, Collection, Compressible, CompressionProof, Key, SolanaAccount},
utils::{close_program_account, fetch_core_data, load_key, verify_proof},
};

#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
Expand Down Expand Up @@ -45,82 +44,18 @@ pub(crate) fn burn<'a>(accounts: &'a [AccountInfo<'a>], args: BurnArgs) -> Progr
asset.wrap()?;
}
Key::Asset => {
let (_, _, plugin_registry) = fetch_core_data::<Asset>(ctx.accounts.asset)?;

let mut checks: BTreeMap<PluginType, (Key, CheckResult, RegistryRecord)> =
BTreeMap::new();

// The asset approval overrides the collection approval.
let asset_approval = Asset::check_burn();
let core_check = match asset_approval {
CheckResult::None => (Key::Collection, Collection::check_burn()),
_ => (Key::Asset, asset_approval),
};

// Check the collection plugins first.
ctx.accounts.collection.and_then(|collection_info| {
fetch_core_data::<Collection>(collection_info)
.map(|(_, _, registry)| {
registry.map(|r| {
r.check_registry(Key::Collection, PluginType::check_burn, &mut checks);
r
})
})
.ok()?
});

// Next check the asset plugins. Plugins on the asset override the collection plugins,
// so we don't need to validate the collection plugins if the asset has a plugin.
if let Some(registry) = plugin_registry.as_ref() {
registry.check_registry(Key::Asset, PluginType::check_burn, &mut checks);
}

solana_program::msg!("checks: {:#?}", checks);

// Do the core validation.
let mut approved = matches!(
core_check,
(
Key::Asset | Key::Collection,
CheckResult::CanApprove | CheckResult::CanReject
)
) && {
(match core_check.0 {
Key::Collection => Collection::load(
ctx.accounts
.collection
.ok_or(MplCoreError::InvalidCollection)?,
0,
)?
.validate_burn(ctx.accounts.authority)?,
Key::Asset => {
Asset::load(ctx.accounts.asset, 0)?.validate_burn(ctx.accounts.authority)?
}
_ => return Err(MplCoreError::IncorrectAccount.into()),
}) == ValidationResult::Approved
};

approved = validate_plugin_checks(
Key::Collection,
&checks,
ctx.accounts.authority,
ctx.accounts.asset,
ctx.accounts.collection,
Box::new(Plugin::validate_burn),
)? || approved;

approved = validate_plugin_checks(
Key::Asset,
&checks,
let _ = validate_asset_permissions(
ctx.accounts.authority,
ctx.accounts.asset,
ctx.accounts.collection,
Box::new(Plugin::validate_burn),
)? || approved;

if !approved {
return Err(MplCoreError::InvalidAuthority.into());
}
None,
Asset::check_burn,
Collection::check_burn,
PluginType::check_burn,
Asset::validate_burn,
Collection::validate_burn,
Plugin::validate_burn,
)?;
}
_ => return Err(MplCoreError::IncorrectAccount.into()),
}
Expand All @@ -146,47 +81,14 @@ pub(crate) fn burn_collection<'a>(
assert_signer(payer)?;
}

let (collection, _, plugin_registry) = fetch_core_data::<Collection>(ctx.accounts.collection)?;

// let checks: [(Key, CheckResult); PluginType::COUNT + 2];

let mut approved = false;
match Collection::check_burn() {
CheckResult::CanApprove | CheckResult::CanReject => {
match collection.validate_burn(ctx.accounts.authority)? {
ValidationResult::Approved => {
approved = true;
}
ValidationResult::Rejected => return Err(MplCoreError::InvalidAuthority.into()),
ValidationResult::Pass => (),
}
}
CheckResult::None => (),
};

if let Some(plugin_registry) = plugin_registry {
for record in plugin_registry.registry {
if matches!(
PluginType::check_burn(&record.plugin_type),
CheckResult::CanApprove | CheckResult::CanReject
) {
let result = Plugin::validate_burn(
&Plugin::load(ctx.accounts.collection, record.offset)?,
ctx.accounts.authority,
&record.authority,
)?;
if result == ValidationResult::Rejected {
return Err(MplCoreError::InvalidAuthority.into());
} else if result == ValidationResult::Approved {
approved = true;
}
}
}
};

if !approved {
return Err(MplCoreError::InvalidAuthority.into());
}
let _ = validate_collection_permissions(
ctx.accounts.authority,
ctx.accounts.collection,
Collection::check_burn,
PluginType::check_burn,
Collection::validate_burn,
Plugin::validate_burn,
)?;

process_burn(ctx.accounts.collection, ctx.accounts.authority)
}
Expand Down
Loading

0 comments on commit 5f21cd4

Please sign in to comment.