Skip to content

Commit 2974f02

Browse files
authored
Test validator: Add Core BPF program binaries (anza-xyz#4791)
* program-test: add solana-sdk-ids * program-test: add core bpf program binaries * test-validator: add core bpf program binaries
1 parent 2601d86 commit 2974f02

File tree

12 files changed

+152
-32
lines changed

12 files changed

+152
-32
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

program-test/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ solana-program-runtime = { workspace = true }
3232
solana-runtime = { workspace = true }
3333
solana-sbpf = { workspace = true }
3434
solana-sdk = { workspace = true }
35+
solana-sdk-ids = { workspace = true }
3536
solana-svm = { workspace = true }
3637
solana-timings = { workspace = true }
3738
solana-vote-program = { workspace = true }

program-test/src/lib.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ impl ProgramTest {
815815
bootstrap_validator_stake_lamports,
816816
42,
817817
fee_rate_governor,
818-
rent,
818+
rent.clone(),
819819
ClusterType::Development,
820820
std::mem::take(&mut self.genesis_accounts),
821821
);
@@ -866,7 +866,16 @@ impl ProgramTest {
866866
);
867867

868868
// Add commonly-used SPL programs as a convenience to the user
869-
for (program_id, account) in programs::spl_programs(&Rent::default()).iter() {
869+
for (program_id, account) in programs::spl_programs(&rent).iter() {
870+
bank.store_account(program_id, account);
871+
}
872+
873+
// Add migrated Core BPF programs.
874+
for (program_id, account) in programs::core_bpf_programs(&rent, |feature_id| {
875+
genesis_config.accounts.contains_key(feature_id)
876+
})
877+
.iter()
878+
{
870879
bank.store_account(program_id, account);
871880
}
872881

program-test/src/programs.rs

+49-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use solana_sdk::{
22
account::{Account, AccountSharedData},
33
bpf_loader,
44
bpf_loader_upgradeable::{self, get_program_data_address, UpgradeableLoaderState},
5+
feature_set,
56
pubkey::Pubkey,
67
rent::Rent,
78
};
@@ -12,34 +13,57 @@ mod spl_memo_1_0 {
1213
mod spl_memo_3_0 {
1314
solana_sdk::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
1415
}
16+
1517
static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
1618
(
1719
solana_inline_spl::token::ID,
18-
solana_sdk::bpf_loader::ID,
20+
solana_sdk_ids::bpf_loader::ID,
1921
include_bytes!("programs/spl_token-3.5.0.so"),
2022
),
2123
(
2224
solana_inline_spl::token_2022::ID,
23-
solana_sdk::bpf_loader_upgradeable::ID,
25+
solana_sdk_ids::bpf_loader_upgradeable::ID,
2426
include_bytes!("programs/spl_token_2022-5.0.2.so"),
2527
),
2628
(
2729
spl_memo_1_0::ID,
28-
solana_sdk::bpf_loader::ID,
30+
solana_sdk_ids::bpf_loader::ID,
2931
include_bytes!("programs/spl_memo-1.0.0.so"),
3032
),
3133
(
3234
spl_memo_3_0::ID,
33-
solana_sdk::bpf_loader::ID,
35+
solana_sdk_ids::bpf_loader::ID,
3436
include_bytes!("programs/spl_memo-3.0.0.so"),
3537
),
3638
(
3739
solana_inline_spl::associated_token_account::ID,
38-
solana_sdk::bpf_loader::ID,
40+
solana_sdk_ids::bpf_loader::ID,
3941
include_bytes!("programs/spl_associated_token_account-1.1.1.so"),
4042
),
4143
];
4244

45+
// Programs that were previously builtins but have been migrated to Core BPF.
46+
// All Core BPF programs are owned by BPF loader v3.
47+
// Note the second pubkey is the migration feature ID.
48+
static CORE_BPF_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
49+
(
50+
solana_sdk_ids::address_lookup_table::ID,
51+
feature_set::migrate_address_lookup_table_program_to_core_bpf::ID,
52+
include_bytes!("programs/core_bpf_address_lookup_table-3.0.0.so"),
53+
),
54+
(
55+
solana_sdk_ids::config::ID,
56+
feature_set::migrate_config_program_to_core_bpf::ID,
57+
include_bytes!("programs/core_bpf_config-3.0.0.so"),
58+
),
59+
(
60+
solana_sdk_ids::feature::ID,
61+
feature_set::migrate_feature_gate_program_to_core_bpf::ID,
62+
include_bytes!("programs/core_bpf_feature_gate-0.0.1.so"),
63+
),
64+
// Add more programs here post-migration...
65+
];
66+
4367
/// Returns a tuple `(Pubkey, Account)` for a BPF program, where the key is the
4468
/// provided program ID and the account is a valid BPF Loader program account
4569
/// containing the ELF.
@@ -111,7 +135,7 @@ pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
111135
.iter()
112136
.flat_map(|(program_id, loader_id, elf)| {
113137
let mut accounts = vec![];
114-
if loader_id.eq(&solana_sdk::bpf_loader_upgradeable::ID) {
138+
if loader_id.eq(&solana_sdk_ids::bpf_loader_upgradeable::ID) {
115139
for (key, account) in bpf_loader_upgradeable_program_accounts(program_id, elf, rent)
116140
{
117141
accounts.push((key, AccountSharedData::from(account)));
@@ -124,3 +148,22 @@ pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
124148
})
125149
.collect()
126150
}
151+
152+
pub fn core_bpf_programs<F>(rent: &Rent, is_feature_active: F) -> Vec<(Pubkey, AccountSharedData)>
153+
where
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 is_feature_active(feature_id) {
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+
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

program-test/tests/core_bpf.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,44 @@
11
use {
2-
solana_program_test::ProgramTest,
2+
solana_program_test::{ProgramTest, ProgramTestContext},
33
solana_sdk::{
4-
bpf_loader_upgradeable, instruction::Instruction, signature::Signer,
4+
bpf_loader_upgradeable, instruction::Instruction, pubkey::Pubkey, signature::Signer,
55
transaction::Transaction,
66
},
77
};
88

9+
async fn assert_bpf_program(context: &ProgramTestContext, program_id: &Pubkey) {
10+
let program_account = context
11+
.banks_client
12+
.get_account(*program_id)
13+
.await
14+
.unwrap()
15+
.unwrap();
16+
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
17+
assert!(program_account.executable);
18+
}
19+
920
#[tokio::test]
10-
async fn test_add_bpf_program() {
11-
// Core BPF program: Address Lookup Lable.
12-
let program_id = solana_sdk::address_lookup_table::program::id();
21+
async fn test_vended_core_bpf_programs() {
22+
let program_test = ProgramTest::default();
23+
let context = program_test.start_with_context().await;
24+
25+
assert_bpf_program(&context, &solana_sdk_ids::address_lookup_table::id()).await;
26+
assert_bpf_program(&context, &solana_sdk_ids::config::id()).await;
27+
assert_bpf_program(&context, &solana_sdk_ids::feature::id()).await;
28+
}
29+
30+
#[tokio::test]
31+
async fn test_add_core_bpf_program_manually() {
32+
// Core BPF program: Stake.
33+
let program_id = solana_sdk_ids::stake::id();
1334

1435
let mut program_test = ProgramTest::default();
1536
program_test.add_upgradeable_program_to_genesis("noop_program", &program_id);
1637

1738
let context = program_test.start_with_context().await;
1839

1940
// Assert the program is a BPF Loader Upgradeable program.
20-
let program_account = context
21-
.banks_client
22-
.get_account(program_id)
23-
.await
24-
.unwrap()
25-
.unwrap();
26-
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
41+
assert_bpf_program(&context, &program_id).await;
2742

2843
// Invoke the program.
2944
let instruction = Instruction::new_with_bytes(program_id, &[], Vec::new());

programs/sbf/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

svm/examples/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test-validator/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@ solana-streamer = { workspace = true }
3636
solana-tpu-client = { workspace = true }
3737
tokio = { workspace = true, features = ["full"] }
3838

39+
[dev-dependencies]
40+
solana-sdk-ids = { workspace = true }
41+
3942
[package.metadata.docs.rs]
4043
targets = ["x86_64-unknown-linux-gnu"]

test-validator/src/lib.rs

+57-12
Original file line numberDiff line numberDiff line change
@@ -787,10 +787,30 @@ impl TestValidator {
787787
let validator_stake_lamports = sol_to_lamports(1_000_000.);
788788
let mint_lamports = sol_to_lamports(500_000_000.);
789789

790+
// Only activate features which are not explicitly deactivated.
791+
let mut feature_set = FeatureSet::default().inactive;
792+
for feature in &config.deactivate_feature_set {
793+
if feature_set.remove(feature) {
794+
info!("Feature for {:?} deactivated", feature)
795+
} else {
796+
warn!(
797+
"Feature {:?} set for deactivation is not a known Feature public key",
798+
feature,
799+
)
800+
}
801+
}
802+
790803
let mut accounts = config.accounts.clone();
791804
for (address, account) in solana_program_test::programs::spl_programs(&config.rent) {
792805
accounts.entry(address).or_insert(account);
793806
}
807+
for (address, account) in
808+
solana_program_test::programs::core_bpf_programs(&config.rent, |feature_id| {
809+
feature_set.contains(feature_id)
810+
})
811+
{
812+
accounts.entry(address).or_insert(account);
813+
}
794814
for upgradeable_program in &config.upgradeable_programs {
795815
let data = solana_program_test::read_file(&upgradeable_program.program_path);
796816
let (programdata_address, _) = Pubkey::find_program_address(
@@ -853,18 +873,6 @@ impl TestValidator {
853873
genesis_config.ticks_per_slot = ticks_per_slot;
854874
}
855875

856-
// Only activate features which are not explicitly deactivated.
857-
let mut feature_set = FeatureSet::default().inactive;
858-
for feature in &config.deactivate_feature_set {
859-
if feature_set.remove(feature) {
860-
info!("Feature for {:?} deactivated", feature)
861-
} else {
862-
warn!(
863-
"Feature {:?} set for deactivation is not a known Feature public key",
864-
feature,
865-
)
866-
}
867-
}
868876
for feature in feature_set {
869877
genesis_utils::activate_feature(&mut genesis_config, feature);
870878
}
@@ -1295,4 +1303,41 @@ mod test {
12951303
let feature_state: Feature = bincode::deserialize(feature_account.data()).unwrap();
12961304
assert!(feature_state.activated_at.is_some());
12971305
}
1306+
1307+
#[tokio::test]
1308+
async fn test_core_bpf_programs() {
1309+
let (test_validator, _payer) = TestValidatorGenesis::default()
1310+
.deactivate_features(&[
1311+
// Don't migrate the config program.
1312+
solana_sdk::feature_set::migrate_config_program_to_core_bpf::id(),
1313+
])
1314+
.start_async()
1315+
.await;
1316+
1317+
let rpc_client = test_validator.get_async_rpc_client();
1318+
1319+
let fetched_programs = rpc_client
1320+
.get_multiple_accounts(&[
1321+
solana_sdk_ids::address_lookup_table::id(),
1322+
solana_sdk_ids::config::id(),
1323+
solana_sdk_ids::feature::id(),
1324+
])
1325+
.await
1326+
.unwrap();
1327+
1328+
// Address lookup table is a BPF program.
1329+
let account = fetched_programs[0].as_ref().unwrap();
1330+
assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1331+
assert!(account.executable);
1332+
1333+
// Config is a builtin.
1334+
let account = fetched_programs[1].as_ref().unwrap();
1335+
assert_eq!(account.owner, solana_sdk_ids::native_loader::id());
1336+
assert!(account.executable);
1337+
1338+
// Feature Gate is a BPF program.
1339+
let account = fetched_programs[2].as_ref().unwrap();
1340+
assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
1341+
assert!(account.executable);
1342+
}
12981343
}

0 commit comments

Comments
 (0)