Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 17 additions & 12 deletions program/src/processor/close.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};

use crate::state::AccountDiscriminator;
use crate::state::{buffer::Buffer, AccountDiscriminator};

use super::{validate_authority, validate_metadata};

Expand All @@ -15,6 +15,10 @@ pub fn close(accounts: &[AccountInfo]) -> ProgramResult {
return Err(ProgramError::NotEnoughAccountKeys);
};

if !authority.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}

let account_data = if account.data_is_empty() {
return Err(ProgramError::UninitializedAccount);
} else {
Expand All @@ -25,19 +29,20 @@ pub fn close(accounts: &[AccountInfo]) -> ProgramResult {
// - account: program owned is implicitly checked since we are writing
// to the account

match AccountDiscriminator::try_from(account_data[0])? {
AccountDiscriminator::Buffer => {
// The authority of a buffer account must be the buffer account
// itself.
if !(account.key() == authority.key() && authority.is_signer()) {
return Err(ProgramError::MissingRequiredSignature);
// We only need to validate the authority if it is not a keypair buffer,
// since we already validated that the authority is a signer.
if account.key() != authority.key() {
match AccountDiscriminator::try_from(account_data[0])? {
AccountDiscriminator::Buffer => {
let buffer = unsafe { Buffer::load_unchecked(account_data) };
validate_authority(buffer, authority, program, program_data)?
}
AccountDiscriminator::Metadata => {
let header = validate_metadata(account)?;
validate_authority(header, authority, program, program_data)?
}
_ => return Err(ProgramError::InvalidAccountData),
}
AccountDiscriminator::Metadata => {
let header = validate_metadata(account)?;
validate_authority(header, authority, program, program_data)?
}
_ => return Err(ProgramError::InvalidAccountData),
}

// Move the lamports to the destination account.
Expand Down
20 changes: 11 additions & 9 deletions program/src/processor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use pinocchio::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};

use crate::state::{header::Header, AccountDiscriminator};
use crate::state::{header::Header, Account, AccountDiscriminator};

pub mod allocate;
pub mod close;
Expand Down Expand Up @@ -113,8 +113,8 @@ fn validate_metadata(metadata: &AccountInfo) -> Result<&Header, ProgramError> {
/// - [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,
fn validate_authority<T: Account>(
account: &T,
authority: &AccountInfo,
program: &AccountInfo,
program_data: &AccountInfo,
Expand All @@ -123,19 +123,21 @@ fn validate_authority(
if !authority.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}

// The authority is the set authority.
let explicitly_authorized = match metadata_header.authority.as_ref() {
let explicitly_authorized = match account.get_authority() {
Some(metadata_authority) => metadata_authority == authority.key(),
None => false,
};

// The authority is the program upgrade authority for canonical metadata accounts.
let authorized = explicitly_authorized
|| (metadata_header.canonical()
&& program.key() == &metadata_header.program
|| (account.is_canonical(program.key())
&& is_program_authority(program, program_data, authority.key())?);

if !authorized {
return Err(ProgramError::IncorrectAuthority);
Err(ProgramError::IncorrectAuthority)
} else {
Ok(())
}

Ok(())
}
12 changes: 11 additions & 1 deletion program/src/state/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use pinocchio::{program_error::ProgramError, pubkey::Pubkey};

use super::{AccountDiscriminator, ZeroableOption, SEED_LEN};
use super::{Account, AccountDiscriminator, ZeroableOption, SEED_LEN};

/// Buffer account header.
#[repr(C)]
Expand Down Expand Up @@ -65,3 +65,13 @@ impl Buffer {
&mut *(bytes.as_mut_ptr() as *mut Self)
}
}

impl Account for Buffer {
fn get_authority(&self) -> Option<&Pubkey> {
self.authority.as_ref()
}

fn is_canonical(&self, program: &Pubkey) -> bool {
self.canonical() && self.program.as_ref() == Some(program)
}
}
13 changes: 12 additions & 1 deletion program/src/state/header.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use pinocchio::{program_error::ProgramError, pubkey::Pubkey};

use super::{
AccountDiscriminator, Compression, DataSource, Encoding, Format, ZeroableOption, SEED_LEN,
Account, AccountDiscriminator, Compression, DataSource, Encoding, Format, ZeroableOption,
SEED_LEN,
};

/// Metadata account header.
Expand Down Expand Up @@ -106,3 +107,13 @@ impl Header {
&mut *(bytes.as_mut_ptr() as *mut Self)
}
}

impl Account for Header {
fn get_authority(&self) -> Option<&Pubkey> {
self.authority.as_ref()
}

fn is_canonical(&self, program: &Pubkey) -> bool {
self.canonical() && self.program == *program
}
}
10 changes: 10 additions & 0 deletions program/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ impl<'a> Metadata<'a> {
}
}

/// Utility trait for an account.
pub(crate) trait Account {
/// Returns the account authority, if there is one.
fn get_authority(&self) -> Option<&Pubkey>;

/// Indicates whether the PDA represents a canonical PDA for
/// the given program.
fn is_canonical(&self, program: &Pubkey) -> bool;
}

/// Account discriminators.
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
Expand Down
Loading