Skip to content

Gate Initialize so it can't be frontrun #86

Description

@kaze-cow

Claude recommended this code so that we can gate Initialize using the upgrade authority of the program:

use pinocchio::{
    account_info::AccountInfo,
    program_error::ProgramError,
    pubkey::Pubkey,
    ProgramResult,
};

const BPF_LOADER_UPGRADEABLE_ID: Pubkey =
    pinocchio_pubkey::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111");

/// Assert `authority` is the upgrade authority of THIS program.
/// Expects the program's own executable account, its ProgramData account,
/// and the claimed authority (signer).
fn assert_upgrade_authority(
    program: &AccountInfo,
    program_data: &AccountInfo,
    authority: &AccountInfo,
) -> ProgramResult {
    // 1. Signer actually signed.
    if !authority.is_signer() {
        return Err(ProgramError::MissingRequiredSignature);
    }

    // 2. `program` is our own program account.
    if program.key() != &crate::ID {
        return Err(ProgramError::IncorrectProgramId);
    }

    // 3. Both loader accounts owned by BPFLoaderUpgradeable.
    if !program.is_owned_by(&BPF_LOADER_UPGRADEABLE_ID)
        || !program_data.is_owned_by(&BPF_LOADER_UPGRADEABLE_ID)
    {
        return Err(ProgramError::InvalidAccountOwner);
    }

    // 4. Program account must point at the ProgramData we were handed.
    //    Layout: [u32 discriminant = 2][programdata_address: Pubkey(32)]
    {
        let data = program.try_borrow_data()?;
        if data.len() < 36 || data[0..4] != [2, 0, 0, 0] {
            return Err(ProgramError::InvalidAccountData);
        }
        if &data[4..36] != program_data.key().as_ref() {
            return Err(ProgramError::InvalidAccountData);
        }
    }

    // 5. ProgramData: upgrade authority must be Some(authority).
    //    Layout: [u32 discriminant = 3][slot: u64][Option<Pubkey>]
    //            option tag @ 12, pubkey @ 13..45
    {
        let data = program_data.try_borrow_data()?;
        if data.len() < 45 || data[0..4] != [3, 0, 0, 0] {
            return Err(ProgramError::InvalidAccountData);
        }
        if data[12] != 1 {
            // None => program is immutable, nobody qualifies
            return Err(ProgramError::InvalidAccountData);
        }
        if &data[13..45] != authority.key().as_ref() {
            return Err(ProgramError::InvalidAccountData);
        }
    }

    Ok(())
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions