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