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(())
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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