From c3757d291f2cc66d39e7e647170a1bf9ce925679 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Mon, 13 Jan 2025 17:07:17 +0100 Subject: [PATCH] [wip]: Extract `validate_metadata` from `validate_authority` --- program/src/processor/close.rs | 6 ++-- program/src/processor/mod.rs | 36 +++++++++++-------- program/src/processor/set_authority.rs | 5 +-- program/src/processor/set_data.rs | 5 +-- program/src/processor/set_immutable.rs | 5 +-- .../src/processor/withdraw_excess_lamports.rs | 5 +-- 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/program/src/processor/close.rs b/program/src/processor/close.rs index ad6a8fe..d879e39 100644 --- a/program/src/processor/close.rs +++ b/program/src/processor/close.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use crate::state::AccountDiscriminator; -use super::validate_authority; +use super::{validate_authority, validate_metadata}; /// Closes a program-owned account. /// @@ -34,8 +34,8 @@ pub fn close(accounts: &[AccountInfo]) -> ProgramResult { } } AccountDiscriminator::Metadata => { - // Metadata and authority validation is done in the `validate_authority`. - validate_authority(account, authority, program, program_data)? + let header = validate_metadata(account)?; + validate_authority(header, authority, program, program_data)? } _ => return Err(ProgramError::InvalidAccountData), } diff --git a/program/src/processor/mod.rs b/program/src/processor/mod.rs index 6cdd771..e01b4aa 100644 --- a/program/src/processor/mod.rs +++ b/program/src/processor/mod.rs @@ -82,24 +82,15 @@ fn is_program_authority( Ok(is_program_authority) } -/// Ensures the `metadata` account can be updated by the provided `authority`. +/// Ensures the `metadata` account is valid and mutable. /// /// ## Validation /// The following validation checks are performed: /// /// - [explicit] The `metadata` account discriminator (first byte) must be `2` — i.e. defining a `Metadata` account. /// - [explicit] The `metadata` account must be mutable — i.e. `mutable = true`. -/// - [explicit] The `authority` account must be a signer. -/// - [explicit] The `authority` account must match the authority set on the `metadata` account OR -/// it must be the program upgrade authority if the `metadata` account is canonical (see `is_program_authority`). #[inline(always)] -fn validate_authority( - metadata: &AccountInfo, - authority: &AccountInfo, - program: &AccountInfo, - program_data: &AccountInfo, -) -> Result<(), ProgramError> { - // Metadata checks. +fn validate_metadata(metadata: &AccountInfo) -> Result<&Header, ProgramError> { let header = unsafe { Header::load_unchecked(metadata.borrow_data_unchecked()) }; if header.discriminator != AccountDiscriminator::Metadata as u8 { return Err(ProgramError::UninitializedAccount); @@ -108,20 +99,37 @@ fn validate_authority( // TODO: use custom error (immutable metadata account) return Err(ProgramError::InvalidAccountData); } + Ok(header) +} +/// Ensures the `metadata` account can be updated by the provided `authority`. +/// +/// ## Validation +/// The following validation checks are performed: +/// +/// - [explicit] The `authority` account must be a signer. +/// - [explicit] The `authority` account must match the authority set on the `metadata` account OR +/// it must be the program upgrade authority if the `metadata` account is canonical (see `is_program_authority`). +#[inline(always)] +fn validate_authority( + metadata_header: &Header, + authority: &AccountInfo, + program: &AccountInfo, + program_data: &AccountInfo, +) -> Result<(), ProgramError> { // Authority checks. if !authority.is_signer() { return Err(ProgramError::MissingRequiredSignature); } // The authority is the set authority. - let explicitly_authorized = match header.authority.as_ref() { + let explicitly_authorized = match metadata_header.authority.as_ref() { Some(metadata_authority) => metadata_authority == authority.key(), None => false, }; // The authority is the program upgrade authority for canonical metadata accounts. let authorized = explicitly_authorized - || (header.canonical() - && program.key() == &header.program + || (metadata_header.canonical() + && program.key() == &metadata_header.program && is_program_authority(program, program_data, authority.key())?); if !authorized { return Err(ProgramError::IncorrectAuthority); diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index 9040426..0f656b3 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -4,7 +4,7 @@ use pinocchio::{ use crate::state::{header::Header, Zeroable}; -use super::validate_authority; +use super::{validate_authority, validate_metadata}; /// Sets the authority of a metadata account. /// @@ -24,7 +24,8 @@ pub fn set_authority(accounts: &[AccountInfo], instruction_data: &[u8]) -> Progr // Accounts validation is done in the `validate_authority` function. // - metadata: program owned is implicitly checked since we are writing to // the account - validate_authority(metadata, authority, program, program_data)?; + let header = validate_metadata(metadata)?; + validate_authority(header, authority, program, program_data)?; let header = unsafe { Header::load_mut_unchecked(metadata.borrow_mut_data_unchecked()) }; diff --git a/program/src/processor/set_data.rs b/program/src/processor/set_data.rs index f070b08..1466778 100644 --- a/program/src/processor/set_data.rs +++ b/program/src/processor/set_data.rs @@ -6,7 +6,7 @@ use crate::state::{ header::Header, AccountDiscriminator, Compression, DataSource, Encoding, Format, }; -use super::validate_authority; +use super::{validate_authority, validate_metadata}; /// Update the data of a metadata account. pub fn set_data(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { @@ -24,7 +24,8 @@ pub fn set_data(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramRes }; // Validate authority. - validate_authority(metadata, authority, program, program_data)?; + let header = validate_metadata(metadata)?; + validate_authority(header, authority, program, program_data)?; // Validate metadata account. let metadata_account_data = unsafe { metadata.borrow_mut_data_unchecked() }; diff --git a/program/src/processor/set_immutable.rs b/program/src/processor/set_immutable.rs index 93f0b2b..d0c4931 100644 --- a/program/src/processor/set_immutable.rs +++ b/program/src/processor/set_immutable.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use crate::state::header::Header; -use super::validate_authority; +use super::{validate_authority, validate_metadata}; /// Sets the metadata account as immutable. /// @@ -16,7 +16,8 @@ pub fn set_immutable(accounts: &[AccountInfo]) -> ProgramResult { }; // Accounts validation is done in the `validate_authority` function. - validate_authority(metadata, authority, program, program_data)?; + let header = validate_metadata(metadata)?; + validate_authority(header, authority, program, program_data)?; // Make the metadata account immutable. diff --git a/program/src/processor/withdraw_excess_lamports.rs b/program/src/processor/withdraw_excess_lamports.rs index 44979e0..4605afe 100644 --- a/program/src/processor/withdraw_excess_lamports.rs +++ b/program/src/processor/withdraw_excess_lamports.rs @@ -2,7 +2,7 @@ use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, sysvars::rent::Rent, ProgramResult, }; -use super::validate_authority; +use super::{validate_authority, validate_metadata}; /// Withdraws excess lamports from a metadata account. /// @@ -16,7 +16,8 @@ pub fn withdraw_excess_lamports(accounts: &[AccountInfo]) -> ProgramResult { }; // Accounts validation is done in the `validate_authority` function. - validate_authority(metadata, authority, program, program_data)?; + let header = validate_metadata(metadata)?; + validate_authority(header, authority, program, program_data)?; // Withdraw the excess lamports in the account.