solana_program_test/
programs.rs

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