Skip to content

Commit

Permalink
[wip]: Extract validate_metadata from validate_authority
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Jan 13, 2025
1 parent 46142c5 commit c3757d2
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 25 deletions.
6 changes: 3 additions & 3 deletions program/src/processor/close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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),
}
Expand Down
36 changes: 22 additions & 14 deletions program/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions program/src/processor/set_authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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()) };

Expand Down
5 changes: 3 additions & 2 deletions program/src/processor/set_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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() };
Expand Down
5 changes: 3 additions & 2 deletions program/src/processor/set_immutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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.

Expand Down
5 changes: 3 additions & 2 deletions program/src/processor/withdraw_excess_lamports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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.

Expand Down

0 comments on commit c3757d2

Please sign in to comment.