Skip to main content

solana_program_binaries/
lib.rs

1#![cfg(feature = "agave-unstable-api")]
2#![allow(clippy::arithmetic_side_effects)]
3
4use {
5    solana_account::{Account, AccountSharedData, ReadableAccount},
6    solana_loader_v3_interface::{get_program_data_address, state::UpgradeableLoaderState},
7    solana_pubkey::Pubkey,
8    solana_rent::Rent,
9    solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
10};
11
12mod spl_memo_1_0 {
13    solana_pubkey::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo");
14}
15mod spl_memo_3_0 {
16    solana_pubkey::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
17}
18mod spl_memo_4_0 {
19    solana_pubkey::declare_id!("Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH");
20}
21
22static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
23    (
24        spl_generic_token::token::ID,
25        solana_sdk_ids::bpf_loader_upgradeable::ID,
26        include_bytes!("programs/spl_p_token-1.0.0.so"),
27    ),
28    (
29        spl_generic_token::token_2022::ID,
30        solana_sdk_ids::bpf_loader_upgradeable::ID,
31        include_bytes!("programs/spl_token_2022-10.0.0.so"),
32    ),
33    (
34        spl_memo_1_0::ID,
35        solana_sdk_ids::bpf_loader::ID,
36        include_bytes!("programs/spl_memo-1.0.0.so"),
37    ),
38    (
39        spl_memo_3_0::ID,
40        solana_sdk_ids::bpf_loader::ID,
41        include_bytes!("programs/spl_memo-3.0.0.so"),
42    ),
43    (
44        spl_memo_4_0::ID,
45        solana_sdk_ids::bpf_loader_upgradeable::ID,
46        include_bytes!("programs/spl_memo-4.0.0.so"),
47    ),
48    (
49        spl_generic_token::associated_token_account::ID,
50        solana_sdk_ids::bpf_loader::ID,
51        include_bytes!("programs/spl_associated_token_account-1.1.1.so"),
52    ),
53];
54
55// Programs that were previously builtins but have been migrated to Core BPF.
56// All Core BPF programs are owned by BPF loader v3.
57// Note the second pubkey is the migration feature ID. A `None` value denotes
58// activation on all clusters, therefore no feature gate.
59static CORE_BPF_PROGRAMS: &[(Pubkey, Option<Pubkey>, &[u8])] = &[
60    (
61        solana_sdk_ids::address_lookup_table::ID,
62        None,
63        include_bytes!("programs/core_bpf_address_lookup_table-3.0.0.so"),
64    ),
65    (
66        solana_sdk_ids::config::ID,
67        None,
68        include_bytes!("programs/core_bpf_config-3.0.0.so"),
69    ),
70    (
71        solana_sdk_ids::feature::ID,
72        None,
73        include_bytes!("programs/core_bpf_feature_gate-0.0.1.so"),
74    ),
75    (
76        solana_sdk_ids::stake::ID,
77        None,
78        include_bytes!("programs/core_bpf_stake-5.1.0.so"),
79    ),
80    // Add more programs here post-migration...
81];
82
83/// Returns a tuple `(Pubkey, Account)` for a BPF program, where the key is the
84/// provided program ID and the account is a valid BPF Loader program account
85/// containing the ELF.
86pub fn bpf_loader_program_account(
87    program_id: &Pubkey,
88    elf: &[u8],
89    rent: &Rent,
90) -> (Pubkey, Account) {
91    (
92        *program_id,
93        Account {
94            lamports: rent.minimum_balance(elf.len()).max(1),
95            data: elf.to_vec(),
96            owner: bpf_loader::id(),
97            executable: true,
98            rent_epoch: u64::MAX,
99        },
100    )
101}
102
103/// Returns two tuples `(Pubkey, Account)` for a BPF upgradeable program.
104/// The first tuple is the program account. It contains the provided program ID
105/// and an account with a pointer to its program data account.
106/// The second tuple is the program data account. It contains the program data
107/// address and an account with the program data - a valid BPF Loader Upgradeable
108/// program data account containing the ELF.
109pub fn bpf_loader_upgradeable_program_accounts(
110    program_id: &Pubkey,
111    elf: &[u8],
112    upgrade_authority_address: &Pubkey,
113    rent: &Rent,
114) -> [(Pubkey, Account); 2] {
115    let programdata_address = get_program_data_address(program_id);
116    let program_account = {
117        let space = UpgradeableLoaderState::size_of_program();
118        let lamports = rent.minimum_balance(space);
119        let data = bincode::serialize(&UpgradeableLoaderState::Program {
120            programdata_address,
121        })
122        .unwrap();
123        Account {
124            lamports,
125            data,
126            owner: bpf_loader_upgradeable::id(),
127            executable: true,
128            rent_epoch: u64::MAX,
129        }
130    };
131    let programdata_account = {
132        let space = UpgradeableLoaderState::size_of_programdata_metadata() + elf.len();
133        let lamports = rent.minimum_balance(space);
134        let mut data = bincode::serialize(&UpgradeableLoaderState::ProgramData {
135            slot: 0,
136            upgrade_authority_address: Some(*upgrade_authority_address),
137        })
138        .unwrap();
139        data.extend_from_slice(elf);
140        Account {
141            lamports,
142            data,
143            owner: bpf_loader_upgradeable::id(),
144            executable: false,
145            rent_epoch: u64::MAX,
146        }
147    };
148    [
149        (*program_id, program_account),
150        (programdata_address, programdata_account),
151    ]
152}
153
154pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
155    SPL_PROGRAMS
156        .iter()
157        .flat_map(|(program_id, loader_id, elf)| {
158            let mut accounts = vec![];
159            if loader_id.eq(&solana_sdk_ids::bpf_loader_upgradeable::ID) {
160                for (key, account) in bpf_loader_upgradeable_program_accounts(
161                    program_id,
162                    elf,
163                    &Pubkey::default(),
164                    rent,
165                ) {
166                    accounts.push((key, AccountSharedData::from(account)));
167                }
168            } else {
169                let (key, account) = bpf_loader_program_account(program_id, elf, rent);
170                accounts.push((key, AccountSharedData::from(account)));
171            }
172            accounts
173        })
174        .collect()
175}
176
177pub fn core_bpf_programs<F>(rent: &Rent, is_feature_active: F) -> Vec<(Pubkey, AccountSharedData)>
178where
179    F: Fn(&Pubkey) -> bool,
180{
181    CORE_BPF_PROGRAMS
182        .iter()
183        .flat_map(|(program_id, feature_id, elf)| {
184            let mut accounts = vec![];
185            if feature_id.is_none() || feature_id.is_some_and(|f| is_feature_active(&f)) {
186                for (key, account) in bpf_loader_upgradeable_program_accounts(
187                    program_id,
188                    elf,
189                    &Pubkey::default(),
190                    rent,
191                ) {
192                    accounts.push((key, AccountSharedData::from(account)));
193                }
194            }
195            accounts
196        })
197        .collect()
198}
199
200pub fn by_id(program_id: &Pubkey, rent: &Rent) -> Option<Vec<(Pubkey, AccountSharedData)>> {
201    let programs = spl_programs(rent);
202    if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
203        let n = num_accounts(programs[i].1.owner());
204        return Some(programs.into_iter().skip(i).take(n).collect());
205    }
206
207    let programs = core_bpf_programs(rent, |_| true);
208    if let Some(i) = programs.iter().position(|(key, _)| key == program_id) {
209        let n = num_accounts(programs[i].1.owner());
210        return Some(programs.into_iter().skip(i).take(n).collect());
211    }
212
213    None
214}
215
216fn num_accounts(owner_id: &Pubkey) -> usize {
217    if *owner_id == bpf_loader_upgradeable::id() {
218        2
219    } else {
220        1
221    }
222}