Skip to content

Commit

Permalink
Test validator: Add Core BPF program binaries (#4791)
Browse files Browse the repository at this point in the history
* program-test: add solana-sdk-ids

* program-test: add core bpf program binaries

* test-validator: add core bpf program binaries
  • Loading branch information
buffalojoec authored Feb 6, 2025
1 parent 2601d86 commit 2974f02
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 32 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions program-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ solana-program-runtime = { workspace = true }
solana-runtime = { workspace = true }
solana-sbpf = { workspace = true }
solana-sdk = { workspace = true }
solana-sdk-ids = { workspace = true }
solana-svm = { workspace = true }
solana-timings = { workspace = true }
solana-vote-program = { workspace = true }
Expand Down
13 changes: 11 additions & 2 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ impl ProgramTest {
bootstrap_validator_stake_lamports,
42,
fee_rate_governor,
rent,
rent.clone(),
ClusterType::Development,
std::mem::take(&mut self.genesis_accounts),
);
Expand Down Expand Up @@ -866,7 +866,16 @@ impl ProgramTest {
);

// Add commonly-used SPL programs as a convenience to the user
for (program_id, account) in programs::spl_programs(&Rent::default()).iter() {
for (program_id, account) in programs::spl_programs(&rent).iter() {
bank.store_account(program_id, account);
}

// Add migrated Core BPF programs.
for (program_id, account) in programs::core_bpf_programs(&rent, |feature_id| {
genesis_config.accounts.contains_key(feature_id)
})
.iter()
{
bank.store_account(program_id, account);
}

Expand Down
55 changes: 49 additions & 6 deletions program-test/src/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use solana_sdk::{
account::{Account, AccountSharedData},
bpf_loader,
bpf_loader_upgradeable::{self, get_program_data_address, UpgradeableLoaderState},
feature_set,
pubkey::Pubkey,
rent::Rent,
};
Expand All @@ -12,34 +13,57 @@ mod spl_memo_1_0 {
mod spl_memo_3_0 {
solana_sdk::declare_id!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
}

static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
(
solana_inline_spl::token::ID,
solana_sdk::bpf_loader::ID,
solana_sdk_ids::bpf_loader::ID,
include_bytes!("programs/spl_token-3.5.0.so"),
),
(
solana_inline_spl::token_2022::ID,
solana_sdk::bpf_loader_upgradeable::ID,
solana_sdk_ids::bpf_loader_upgradeable::ID,
include_bytes!("programs/spl_token_2022-5.0.2.so"),
),
(
spl_memo_1_0::ID,
solana_sdk::bpf_loader::ID,
solana_sdk_ids::bpf_loader::ID,
include_bytes!("programs/spl_memo-1.0.0.so"),
),
(
spl_memo_3_0::ID,
solana_sdk::bpf_loader::ID,
solana_sdk_ids::bpf_loader::ID,
include_bytes!("programs/spl_memo-3.0.0.so"),
),
(
solana_inline_spl::associated_token_account::ID,
solana_sdk::bpf_loader::ID,
solana_sdk_ids::bpf_loader::ID,
include_bytes!("programs/spl_associated_token_account-1.1.1.so"),
),
];

// Programs that were previously builtins but have been migrated to Core BPF.
// All Core BPF programs are owned by BPF loader v3.
// Note the second pubkey is the migration feature ID.
static CORE_BPF_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
(
solana_sdk_ids::address_lookup_table::ID,
feature_set::migrate_address_lookup_table_program_to_core_bpf::ID,
include_bytes!("programs/core_bpf_address_lookup_table-3.0.0.so"),
),
(
solana_sdk_ids::config::ID,
feature_set::migrate_config_program_to_core_bpf::ID,
include_bytes!("programs/core_bpf_config-3.0.0.so"),
),
(
solana_sdk_ids::feature::ID,
feature_set::migrate_feature_gate_program_to_core_bpf::ID,
include_bytes!("programs/core_bpf_feature_gate-0.0.1.so"),
),
// Add more programs here post-migration...
];

/// Returns a tuple `(Pubkey, Account)` for a BPF program, where the key is the
/// provided program ID and the account is a valid BPF Loader program account
/// containing the ELF.
Expand Down Expand Up @@ -111,7 +135,7 @@ pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
.iter()
.flat_map(|(program_id, loader_id, elf)| {
let mut accounts = vec![];
if loader_id.eq(&solana_sdk::bpf_loader_upgradeable::ID) {
if loader_id.eq(&solana_sdk_ids::bpf_loader_upgradeable::ID) {
for (key, account) in bpf_loader_upgradeable_program_accounts(program_id, elf, rent)
{
accounts.push((key, AccountSharedData::from(account)));
Expand All @@ -124,3 +148,22 @@ pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> {
})
.collect()
}

pub fn core_bpf_programs<F>(rent: &Rent, is_feature_active: F) -> Vec<(Pubkey, AccountSharedData)>
where
F: Fn(&Pubkey) -> bool,
{
CORE_BPF_PROGRAMS
.iter()
.flat_map(|(program_id, feature_id, elf)| {
let mut accounts = vec![];
if is_feature_active(feature_id) {
for (key, account) in bpf_loader_upgradeable_program_accounts(program_id, elf, rent)
{
accounts.push((key, AccountSharedData::from(account)));
}
}
accounts
})
.collect()
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
39 changes: 27 additions & 12 deletions program-test/tests/core_bpf.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
use {
solana_program_test::ProgramTest,
solana_program_test::{ProgramTest, ProgramTestContext},
solana_sdk::{
bpf_loader_upgradeable, instruction::Instruction, signature::Signer,
bpf_loader_upgradeable, instruction::Instruction, pubkey::Pubkey, signature::Signer,
transaction::Transaction,
},
};

async fn assert_bpf_program(context: &ProgramTestContext, program_id: &Pubkey) {
let program_account = context
.banks_client
.get_account(*program_id)
.await
.unwrap()
.unwrap();
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert!(program_account.executable);
}

#[tokio::test]
async fn test_add_bpf_program() {
// Core BPF program: Address Lookup Lable.
let program_id = solana_sdk::address_lookup_table::program::id();
async fn test_vended_core_bpf_programs() {
let program_test = ProgramTest::default();
let context = program_test.start_with_context().await;

assert_bpf_program(&context, &solana_sdk_ids::address_lookup_table::id()).await;
assert_bpf_program(&context, &solana_sdk_ids::config::id()).await;
assert_bpf_program(&context, &solana_sdk_ids::feature::id()).await;
}

#[tokio::test]
async fn test_add_core_bpf_program_manually() {
// Core BPF program: Stake.
let program_id = solana_sdk_ids::stake::id();

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

let context = program_test.start_with_context().await;

// Assert the program is a BPF Loader Upgradeable program.
let program_account = context
.banks_client
.get_account(program_id)
.await
.unwrap()
.unwrap();
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert_bpf_program(&context, &program_id).await;

// Invoke the program.
let instruction = Instruction::new_with_bytes(program_id, &[], Vec::new());
Expand Down
1 change: 1 addition & 0 deletions programs/sbf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions svm/examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions test-validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,8 @@ solana-streamer = { workspace = true }
solana-tpu-client = { workspace = true }
tokio = { workspace = true, features = ["full"] }

[dev-dependencies]
solana-sdk-ids = { workspace = true }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
69 changes: 57 additions & 12 deletions test-validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,10 +787,30 @@ impl TestValidator {
let validator_stake_lamports = sol_to_lamports(1_000_000.);
let mint_lamports = sol_to_lamports(500_000_000.);

// Only activate features which are not explicitly deactivated.
let mut feature_set = FeatureSet::default().inactive;
for feature in &config.deactivate_feature_set {
if feature_set.remove(feature) {
info!("Feature for {:?} deactivated", feature)
} else {
warn!(
"Feature {:?} set for deactivation is not a known Feature public key",
feature,
)
}
}

let mut accounts = config.accounts.clone();
for (address, account) in solana_program_test::programs::spl_programs(&config.rent) {
accounts.entry(address).or_insert(account);
}
for (address, account) in
solana_program_test::programs::core_bpf_programs(&config.rent, |feature_id| {
feature_set.contains(feature_id)
})
{
accounts.entry(address).or_insert(account);
}
for upgradeable_program in &config.upgradeable_programs {
let data = solana_program_test::read_file(&upgradeable_program.program_path);
let (programdata_address, _) = Pubkey::find_program_address(
Expand Down Expand Up @@ -853,18 +873,6 @@ impl TestValidator {
genesis_config.ticks_per_slot = ticks_per_slot;
}

// Only activate features which are not explicitly deactivated.
let mut feature_set = FeatureSet::default().inactive;
for feature in &config.deactivate_feature_set {
if feature_set.remove(feature) {
info!("Feature for {:?} deactivated", feature)
} else {
warn!(
"Feature {:?} set for deactivation is not a known Feature public key",
feature,
)
}
}
for feature in feature_set {
genesis_utils::activate_feature(&mut genesis_config, feature);
}
Expand Down Expand Up @@ -1295,4 +1303,41 @@ mod test {
let feature_state: Feature = bincode::deserialize(feature_account.data()).unwrap();
assert!(feature_state.activated_at.is_some());
}

#[tokio::test]
async fn test_core_bpf_programs() {
let (test_validator, _payer) = TestValidatorGenesis::default()
.deactivate_features(&[
// Don't migrate the config program.
solana_sdk::feature_set::migrate_config_program_to_core_bpf::id(),
])
.start_async()
.await;

let rpc_client = test_validator.get_async_rpc_client();

let fetched_programs = rpc_client
.get_multiple_accounts(&[
solana_sdk_ids::address_lookup_table::id(),
solana_sdk_ids::config::id(),
solana_sdk_ids::feature::id(),
])
.await
.unwrap();

// Address lookup table is a BPF program.
let account = fetched_programs[0].as_ref().unwrap();
assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
assert!(account.executable);

// Config is a builtin.
let account = fetched_programs[1].as_ref().unwrap();
assert_eq!(account.owner, solana_sdk_ids::native_loader::id());
assert!(account.executable);

// Feature Gate is a BPF program.
let account = fetched_programs[2].as_ref().unwrap();
assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
assert!(account.executable);
}
}

0 comments on commit 2974f02

Please sign in to comment.