diff --git a/Cargo.lock b/Cargo.lock index 3bc6cd4ab40510..9c43ec7af17ef3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3367,6 +3367,7 @@ version = "1.2.27" dependencies = [ "bincode", "byteorder", + "jemalloc-sys", "num-derive 0.3.0", "num-traits", "rand 0.7.3", @@ -3556,6 +3557,7 @@ dependencies = [ "serial_test", "serial_test_derive", "solana-account-decoder", + "solana-bpf-loader-program", "solana-budget-program", "solana-clap-utils", "solana-client", diff --git a/core/Cargo.toml b/core/Cargo.toml index 6f047f012d9de9..056b5aae663229 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -43,6 +43,7 @@ serde = "1.0.110" serde_derive = "1.0.103" serde_json = "1.0.53" solana-account-decoder = { path = "../account-decoder", version = "1.2.27" } +solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.2.27" } solana-budget-program = { path = "../programs/budget", version = "1.2.27" } solana-clap-utils = { path = "../clap-utils", version = "1.2.27" } solana-client = { path = "../client", version = "1.2.27" } diff --git a/core/src/lib.rs b/core/src/lib.rs index 7d63cf64f0e365..a7dde58607c4bf 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -71,6 +71,9 @@ pub mod verified_vote_packets; pub mod weighted_shuffle; pub mod window_service; +#[macro_use] +extern crate solana_bpf_loader_program; + #[macro_use] extern crate solana_budget_program; diff --git a/core/src/validator.rs b/core/src/validator.rs index 58278391bc7d45..be30f05e02de16 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -860,6 +860,13 @@ impl TestValidator { genesis_config .native_instruction_processors .push(solana_budget_program!()); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_program!()); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_deprecated_program!()); + genesis_config.rent.lamports_per_byte_year = 1; genesis_config.rent.exemption_threshold = 1.0; genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0); diff --git a/genesis-programs/src/lib.rs b/genesis-programs/src/lib.rs index 575f4d34e01895..86d2935d382070 100644 --- a/genesis-programs/src/lib.rs +++ b/genesis-programs/src/lib.rs @@ -1,3 +1,7 @@ +use solana_sdk::{ + clock::Epoch, genesis_config::OperatingMode, inflation::Inflation, pubkey::Pubkey, +}; + #[macro_use] extern crate solana_bpf_loader_program; #[macro_use] @@ -9,10 +13,6 @@ extern crate solana_vest_program; use log::*; use solana_runtime::bank::{Bank, EnteredEpochCallback}; -use solana_sdk::{ - clock::Epoch, entrypoint_native::ProcessInstructionWithContext, genesis_config::OperatingMode, - inflation::Inflation, pubkey::Pubkey, -}; pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option { match operating_mode { @@ -44,27 +44,22 @@ pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option Option> { +pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option> { match operating_mode { OperatingMode::Development => { if epoch == 0 { // Programs used for testing Some(vec![ - Program::BuiltinLoader(solana_bpf_loader_program!()), - Program::BuiltinLoader(solana_bpf_loader_deprecated_program!()), - Program::Native(solana_vest_program!()), - Program::Native(solana_budget_program!()), - Program::Native(solana_exchange_program!()), + solana_bpf_loader_program!(), + solana_bpf_loader_deprecated_program!(), + solana_vest_program!(), + solana_budget_program!(), + solana_exchange_program!(), ]) } else if epoch == std::u64::MAX { // The epoch of std::u64::MAX is a placeholder and is expected // to be reduced in a future network update. - Some(vec![Program::BuiltinLoader(solana_bpf_loader_program!())]) + Some(vec![solana_bpf_loader_program!()]) } else { None } @@ -73,10 +68,7 @@ fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option Option Option Option> { - match get_programs(operating_mode, epoch) { - Some(programs) => { - let mut native_programs = vec![]; - for program in programs { - if let Program::Native((string, key)) = program { - native_programs.push((string, key)); - } - } - Some(native_programs) - } - None => None, - } -} - pub fn get_entered_epoch_callback(operating_mode: OperatingMode) -> EnteredEpochCallback { Box::new(move |bank: &mut Bank| { + info!( + "Entering epoch {} with operating_mode {:?}", + bank.epoch(), + operating_mode + ); if let Some(inflation) = get_inflation(operating_mode, bank.epoch()) { info!("Entering new epoch with inflation {:?}", inflation); bank.set_inflation(inflation); } - if let Some(programs) = get_programs(operating_mode, bank.epoch()) { - for program in programs { - match program { - Program::Native((name, program_id)) => { - bank.add_native_program(&name, &program_id); - } - Program::BuiltinLoader(( - name, - program_id, - process_instruction_with_context, - )) => { - bank.add_builtin_loader( - &name, - program_id, - process_instruction_with_context, - ); - } - } + if let Some(new_programs) = get_programs(operating_mode, bank.epoch()) { + for (name, program_id) in new_programs.iter() { + info!("Registering {} at {}", name, program_id); + bank.add_native_program(name, program_id); } } if OperatingMode::Stable == operating_mode { @@ -156,13 +118,8 @@ mod tests { #[test] fn test_id_uniqueness() { let mut unique = HashSet::new(); - let programs = get_programs(OperatingMode::Development, 0).unwrap(); - for program in programs { - match program { - Program::Native((name, id)) => assert!(unique.insert((name, id))), - Program::BuiltinLoader((name, id, _)) => assert!(unique.insert((name, id))), - } - } + let ids = get_programs(OperatingMode::Development, 0).unwrap(); + assert!(ids.into_iter().all(move |id| unique.insert(id))); } #[test] @@ -180,18 +137,7 @@ mod tests { get_programs(OperatingMode::Development, 0).unwrap().len(), 5 ); - assert!(get_programs(OperatingMode::Development, 1).is_none()); - } - - #[test] - fn test_native_development_programs() { - assert_eq!( - get_native_programs(OperatingMode::Development, 0) - .unwrap() - .len(), - 3 - ); - assert!(get_native_programs(OperatingMode::Development, 1).is_none()); + assert_eq!(get_programs(OperatingMode::Development, 1), None); } #[test] @@ -209,7 +155,7 @@ mod tests { #[test] fn test_softlaunch_programs() { - assert!(get_programs(OperatingMode::Stable, 1).is_none()); + assert_eq!(get_programs(OperatingMode::Stable, 1), None); assert!(get_programs(OperatingMode::Stable, std::u64::MAX).is_some()); } } diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 6c4fbf6e6b5f1d..ad2c4d82d02624 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -468,7 +468,7 @@ fn main() -> Result<(), Box> { ); let native_instruction_processors = - solana_genesis_programs::get_native_programs(operating_mode, 0).unwrap_or_else(Vec::new); + solana_genesis_programs::get_programs(operating_mode, 0).unwrap_or_else(|| vec![]); let inflation = solana_genesis_programs::get_inflation(operating_mode, 0).unwrap(); let mut genesis_config = GenesisConfig { diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 2689d4db5538fe..4ec5e41de5238d 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -300,11 +300,11 @@ pub fn process_blockstore( } // Setup bank for slot 0 - let mut bank0 = Bank::new_with_paths(&genesis_config, account_paths, &opts.frozen_accounts); - let callback = - solana_genesis_programs::get_entered_epoch_callback(genesis_config.operating_mode); - callback(&mut bank0); - let bank0 = Arc::new(bank0); + let bank0 = Arc::new(Bank::new_with_paths( + &genesis_config, + account_paths, + &opts.frozen_accounts, + )); info!("processing ledger for slot 0..."); let recyclers = VerifyRecyclers::default(); process_bank_0(&bank0, blockstore, &opts, &recyclers)?; diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index d8eb1f3e9d1973..fc752e18023aab 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -140,7 +140,7 @@ impl LocalCluster { match genesis_config.operating_mode { OperatingMode::Stable | OperatingMode::Preview => { genesis_config.native_instruction_processors = - solana_genesis_programs::get_native_programs(genesis_config.operating_mode, 0) + solana_genesis_programs::get_programs(genesis_config.operating_mode, 0) .unwrap_or_default() } _ => (), diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 827e702ae25a19..3fbab17625215b 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -1569,6 +1569,7 @@ version = "1.2.27" dependencies = [ "bincode", "byteorder 1.3.4", + "jemalloc-sys", "num-derive 0.3.0", "num-traits", "solana-runtime", diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 691f09763fc4e1..0c1f0ece6136bf 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1,435 +1,387 @@ -#![cfg(any(feature = "bpf_c", feature = "bpf_rust"))] - -#[macro_use] -extern crate solana_bpf_loader_program; - -use solana_runtime::{ - bank::Bank, - bank_client::BankClient, - genesis_utils::{create_genesis_config, GenesisConfigInfo}, - loader_utils::load_program, -}; -use solana_sdk::{ - account::Account, - bpf_loader, - client::SyncClient, - clock::DEFAULT_SLOTS_PER_EPOCH, - instruction::{AccountMeta, Instruction, InstructionError}, - message::Message, - pubkey::Pubkey, - signature::Keypair, - signature::Signer, - sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history}, - transaction::TransactionError, -}; -use std::{env, fs::File, io::Read, path::PathBuf, sync::Arc}; - -/// BPF program file extension -const PLATFORM_FILE_EXTENSION_BPF: &str = "so"; - -/// Create a BPF program file name -fn create_bpf_path(name: &str) -> PathBuf { - let mut pathbuf = { - let current_exe = env::current_exe().unwrap(); - PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) - }; - pathbuf.push("bpf/"); - pathbuf.push(name); - pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF); - pathbuf -} - -fn load_bpf_program(bank_client: &BankClient, payer_keypair: &Keypair, name: &str) -> Pubkey { - let path = create_bpf_path(name); - let mut file = File::open(path).unwrap(); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - load_program(bank_client, payer_keypair, &bpf_loader::id(), elf) -} - -#[test] #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))] -fn test_program_bpf_sanity() { - solana_logger::setup(); - - let mut programs = Vec::new(); - #[cfg(feature = "bpf_c")] - { - programs.extend_from_slice(&[ - ("bpf_to_bpf", true), - ("multiple_static", true), - ("noop", true), - ("noop++", true), - ("panic", false), - ("relative_call", true), - ("struct_pass", true), - ("struct_ret", true), - ]); +mod bpf { + use solana_bpf_loader_program::solana_bpf_loader_program; + use solana_runtime::{ + bank::Bank, + bank_client::BankClient, + genesis_utils::{create_genesis_config, GenesisConfigInfo}, + loader_utils::load_program, + }; + use solana_sdk::{ + account::Account, + bpf_loader, + client::SyncClient, + clock::DEFAULT_SLOTS_PER_EPOCH, + instruction::{AccountMeta, Instruction, InstructionError}, + message::Message, + pubkey::Pubkey, + signature::Keypair, + signature::Signer, + sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history}, + transaction::TransactionError, + }; + use std::{env, fs::File, io::Read, path::PathBuf, sync::Arc}; + + /// BPF program file extension + const PLATFORM_FILE_EXTENSION_BPF: &str = "so"; + + /// Create a BPF program file name + fn create_bpf_path(name: &str) -> PathBuf { + let mut pathbuf = { + let current_exe = env::current_exe().unwrap(); + PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) + }; + pathbuf.push("bpf/"); + pathbuf.push(name); + pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF); + pathbuf } - #[cfg(feature = "bpf_rust")] - { - programs.extend_from_slice(&[ - ("solana_bpf_rust_128bit", true), - ("solana_bpf_rust_alloc", true), - ("solana_bpf_rust_dep_crate", true), - ("solana_bpf_rust_external_spend", false), - ("solana_bpf_rust_iter", true), - ("solana_bpf_rust_many_args", true), - ("solana_bpf_rust_noop", true), - ("solana_bpf_rust_panic", false), - ("solana_bpf_rust_param_passing", true), - ("solana_bpf_rust_sysval", true), - ]); + + fn load_bpf_program(bank_client: &BankClient, payer_keypair: &Keypair, name: &str) -> Pubkey { + let path = create_bpf_path(name); + let mut file = File::open(path).unwrap(); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + load_program(bank_client, payer_keypair, &bpf_loader::id(), elf) } - for program in programs.iter() { - println!("Test program: {:?}", program.0); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin_loader(&name, id, entrypoint); - let bank = Arc::new(bank); - - // Create bank with a specific slot, used by solana_bpf_rust_sysvar test - let bank = Bank::new_from_parent(&bank, &Pubkey::default(), DEFAULT_SLOTS_PER_EPOCH + 1); - let bank_client = BankClient::new(bank); - - // Call user program - let program_id = load_bpf_program(&bank_client, &mint_keypair, program.0); - let account_metas = vec![ - AccountMeta::new(mint_keypair.pubkey(), true), - AccountMeta::new(Keypair::new().pubkey(), false), - AccountMeta::new(clock::id(), false), - AccountMeta::new(fees::id(), false), - AccountMeta::new(rewards::id(), false), - AccountMeta::new(slot_hashes::id(), false), - AccountMeta::new(stake_history::id(), false), - AccountMeta::new(rent::id(), false), - ]; - let instruction = Instruction::new(program_id, &1u8, account_metas); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - if program.1 { - assert!(result.is_ok()); - } else { - assert!(result.is_err()); + #[test] + #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))] + fn test_program_bpf_sanity() { + solana_logger::setup(); + + let mut programs = Vec::new(); + #[cfg(feature = "bpf_c")] + { + programs.extend_from_slice(&[ + ("bpf_to_bpf", true), + ("multiple_static", true), + ("noop", true), + ("noop++", true), + ("panic", false), + ("relative_call", true), + ("struct_pass", true), + ("struct_ret", true), + ]); + } + #[cfg(feature = "bpf_rust")] + { + programs.extend_from_slice(&[ + ("solana_bpf_rust_128bit", true), + ("solana_bpf_rust_alloc", true), + ("solana_bpf_rust_dep_crate", true), + ("solana_bpf_rust_external_spend", false), + ("solana_bpf_rust_iter", true), + ("solana_bpf_rust_many_args", true), + ("solana_bpf_rust_noop", true), + ("solana_bpf_rust_panic", false), + ("solana_bpf_rust_param_passing", true), + ("solana_bpf_rust_sysval", true), + ]); + } + + for program in programs.iter() { + println!("Test program: {:?}", program.0); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_program!()); + let bank = Arc::new(Bank::new(&genesis_config)); + // Create bank with a specific slot, used by solana_bpf_rust_sysvar test + let bank = + Bank::new_from_parent(&bank, &Pubkey::default(), DEFAULT_SLOTS_PER_EPOCH + 1); + let bank_client = BankClient::new(bank); + + // Call user program + let program_id = load_bpf_program(&bank_client, &mint_keypair, program.0); + let account_metas = vec![ + AccountMeta::new(mint_keypair.pubkey(), true), + AccountMeta::new(Keypair::new().pubkey(), false), + AccountMeta::new(clock::id(), false), + AccountMeta::new(fees::id(), false), + AccountMeta::new(rewards::id(), false), + AccountMeta::new(slot_hashes::id(), false), + AccountMeta::new(stake_history::id(), false), + AccountMeta::new(rent::id(), false), + ]; + let instruction = Instruction::new(program_id, &1u8, account_metas); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + if program.1 { + assert!(result.is_ok()); + } else { + assert!(result.is_err()); + } } } -} -#[test] -fn test_program_bpf_duplicate_accounts() { - solana_logger::setup(); + #[test] + fn test_program_bpf_duplicate_accounts() { + solana_logger::setup(); - let mut programs = Vec::new(); - #[cfg(feature = "bpf_c")] - { - programs.extend_from_slice(&[("dup_accounts")]); - } - #[cfg(feature = "bpf_rust")] - { - programs.extend_from_slice(&[("solana_bpf_rust_dup_accounts")]); - } + let mut programs = Vec::new(); + #[cfg(feature = "bpf_c")] + { + programs.extend_from_slice(&[("dup_accounts")]); + } + #[cfg(feature = "bpf_rust")] + { + programs.extend_from_slice(&[("solana_bpf_rust_dup_accounts")]); + } - for program in programs.iter() { - println!("Test program: {:?}", program); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin_loader(&name, id, entrypoint); - let bank = Arc::new(bank); - let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program(&bank_client, &mint_keypair, program); - let payee_account = Account::new(10, 1, &program_id); - let payee_pubkey = Pubkey::new_rand(); - bank.store_account(&payee_pubkey, &payee_account); - - let account = Account::new(10, 1, &program_id); - let pubkey = Pubkey::new_rand(); - let account_metas = vec![ - AccountMeta::new(mint_keypair.pubkey(), true), - AccountMeta::new(payee_pubkey, false), - AccountMeta::new(pubkey, false), - AccountMeta::new(pubkey, false), - ]; - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &1u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); - assert!(result.is_ok()); - assert_eq!(data[0], 1); - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &2u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); - assert!(result.is_ok()); - assert_eq!(data[0], 2); - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); - assert!(result.is_ok()); - assert_eq!(data[0], 3); - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &4u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let lamports = bank_client.get_balance(&pubkey).unwrap(); - assert!(result.is_ok()); - assert_eq!(lamports, 11); - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &5u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let lamports = bank_client.get_balance(&pubkey).unwrap(); - assert!(result.is_ok()); - assert_eq!(lamports, 12); - - bank.store_account(&pubkey, &account); - let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let lamports = bank_client.get_balance(&pubkey).unwrap(); - assert!(result.is_ok()); - assert_eq!(lamports, 13); - } -} + for program in programs.iter() { + println!("Test program: {:?}", program); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_program!()); + let bank = Arc::new(Bank::new(&genesis_config)); + let bank_client = BankClient::new_shared(&bank); + let program_id = load_bpf_program(&bank_client, &mint_keypair, program); + let payee_account = Account::new(10, 1, &program_id); + let payee_pubkey = Pubkey::new_rand(); + bank.store_account(&payee_pubkey, &payee_account); + + let account = Account::new(10, 1, &program_id); + let pubkey = Pubkey::new_rand(); + let account_metas = vec![ + AccountMeta::new(mint_keypair.pubkey(), true), + AccountMeta::new(payee_pubkey, false), + AccountMeta::new(pubkey, false), + AccountMeta::new(pubkey, false), + ]; + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &1u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); + assert!(result.is_ok()); + assert_eq!(data[0], 1); -#[test] -fn test_program_bpf_error_handling() { - solana_logger::setup(); + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &2u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); + assert!(result.is_ok()); + assert_eq!(data[0], 2); - let mut programs = Vec::new(); - #[cfg(feature = "bpf_c")] - { - programs.extend_from_slice(&[("error_handling")]); - } - #[cfg(feature = "bpf_rust")] - { - programs.extend_from_slice(&[("solana_bpf_rust_error_handling")]); + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); + assert!(result.is_ok()); + assert_eq!(data[0], 3); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &4u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let lamports = bank_client.get_balance(&pubkey).unwrap(); + assert!(result.is_ok()); + assert_eq!(lamports, 11); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &5u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let lamports = bank_client.get_balance(&pubkey).unwrap(); + assert!(result.is_ok()); + assert_eq!(lamports, 12); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let lamports = bank_client.get_balance(&pubkey).unwrap(); + assert!(result.is_ok()); + assert_eq!(lamports, 13); + } } - for program in programs.iter() { - println!("Test program: {:?}", program); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin_loader(&name, id, entrypoint); - let bank_client = BankClient::new(bank); - let program_id = load_bpf_program(&bank_client, &mint_keypair, program); - let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)]; - - let instruction = Instruction::new(program_id, &1u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert!(result.is_ok()); - - let instruction = Instruction::new(program_id, &2u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::InvalidAccountData) - ); - - let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::Custom(0)) - ); - - let instruction = Instruction::new(program_id, &4u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::Custom(42)) - ); - - let instruction = Instruction::new(program_id, &5u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let result = result.unwrap_err().unwrap(); - if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result + #[test] + fn test_program_bpf_error_handling() { + solana_logger::setup(); + + let mut programs = Vec::new(); + #[cfg(feature = "bpf_c")] + { + programs.extend_from_slice(&[("error_handling")]); + } + #[cfg(feature = "bpf_rust")] { + programs.extend_from_slice(&[("solana_bpf_rust_error_handling")]); + } + + for program in programs.iter() { + println!("Test program: {:?}", program); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_program!()); + let bank = Bank::new(&genesis_config); + let bank_client = BankClient::new(bank); + let program_id = load_bpf_program(&bank_client, &mint_keypair, program); + let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)]; + + let instruction = Instruction::new(program_id, &1u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert!(result.is_ok()); + + let instruction = Instruction::new(program_id, &2u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( - result, - TransactionError::InstructionError(0, InstructionError::InvalidError) + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::InvalidAccountData) ); - } - let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let result = result.unwrap_err().unwrap(); - if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result - { + let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( - result, - TransactionError::InstructionError(0, InstructionError::InvalidError) + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(0)) ); - } - let instruction = Instruction::new(program_id, &7u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - let result = result.unwrap_err().unwrap(); - if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result - { + let instruction = Instruction::new(program_id, &4u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( - result, - TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed) + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(42)) ); - } - let instruction = Instruction::new(program_id, &8u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) - ); - - let instruction = Instruction::new(program_id, &9u8, account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) - ); + let instruction = Instruction::new(program_id, &5u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let result = result.unwrap_err().unwrap(); + if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + != result + { + assert_eq!( + result, + TransactionError::InstructionError(0, InstructionError::InvalidError) + ); + } + + let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let result = result.unwrap_err().unwrap(); + if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + != result + { + assert_eq!( + result, + TransactionError::InstructionError(0, InstructionError::InvalidError) + ); + } + + let instruction = Instruction::new(program_id, &7u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + let result = result.unwrap_err().unwrap(); + if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + != result + { + assert_eq!( + result, + TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed) + ); + } + + let instruction = Instruction::new(program_id, &8u8, account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + ); + } } -} -#[test] -fn test_program_bpf_invoke() { - solana_logger::setup(); + #[test] + fn test_program_bpf_invoke() { + solana_logger::setup(); - const TEST_SUCCESS: u8 = 1; - const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; - const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; + const TEST_SUCCESS: u8 = 1; + const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; + const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; - let mut programs = Vec::new(); - #[cfg(feature = "bpf_c")] - { - programs.extend_from_slice(&[("invoke", "invoked")]); - } - #[cfg(feature = "bpf_rust")] - { - programs.extend_from_slice(&[("solana_bpf_rust_invoke", "solana_bpf_rust_invoked")]); - } + let mut programs = Vec::new(); + #[cfg(feature = "bpf_c")] + { + programs.extend_from_slice(&[("invoke", "invoked")]); + } + #[cfg(feature = "bpf_rust")] + { + programs.extend_from_slice(&[("solana_bpf_rust_invoke", "solana_bpf_rust_invoked")]); + } - for program in programs.iter() { - println!("Test program: {:?}", program); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin_loader(&name, id, entrypoint); - let bank = Arc::new(bank); - let bank_client = BankClient::new_shared(&bank); - - let invoke_program_id = load_bpf_program(&bank_client, &mint_keypair, program.0); - let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1); - - let argument_keypair = Keypair::new(); - let account = Account::new(41, 100, &invoke_program_id); - bank.store_account(&argument_keypair.pubkey(), &account); - - let invoked_argument_keypair = Keypair::new(); - let account = Account::new(10, 10, &invoked_program_id); - bank.store_account(&invoked_argument_keypair.pubkey(), &account); - - let from_keypair = Keypair::new(); - let account = Account::new(43, 0, &solana_sdk::system_program::id()); - bank.store_account(&from_keypair.pubkey(), &account); - - let (derived_key1, nonce1) = - Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id); - let (derived_key2, nonce2) = - Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id); - let (derived_key3, nonce3) = - Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id); - - let mint_pubkey = mint_keypair.pubkey(); - let account_metas = vec![ - AccountMeta::new(mint_pubkey, true), - AccountMeta::new(argument_keypair.pubkey(), true), - AccountMeta::new_readonly(invoked_program_id, false), - AccountMeta::new(invoked_argument_keypair.pubkey(), true), - AccountMeta::new_readonly(invoked_program_id, false), - AccountMeta::new(argument_keypair.pubkey(), true), - AccountMeta::new(derived_key1, false), - AccountMeta::new(derived_key2, false), - AccountMeta::new_readonly(derived_key3, false), - AccountMeta::new_readonly(solana_sdk::system_program::id(), false), - AccountMeta::new(from_keypair.pubkey(), true), - ]; - - // success cases - - let instruction = Instruction::new( - invoke_program_id, - &[TEST_SUCCESS, nonce1, nonce2, nonce3], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - assert!(bank_client - .send_and_confirm_message( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair - ], - message, - ) - .is_ok()); - - // failure cases - - let instruction = Instruction::new( - invoke_program_id, - &TEST_PRIVILEGE_ESCALATION_SIGNER, - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - assert_eq!( - bank_client - .send_and_confirm_message( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair - ], - message, - ) - .unwrap_err() - .unwrap(), - TransactionError::InstructionError(0, InstructionError::Custom(194969602)) - ); - - let instruction = Instruction::new( - invoke_program_id, - &TEST_PRIVILEGE_ESCALATION_WRITABLE, - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - assert_eq!( - bank_client + for program in programs.iter() { + println!("Test program: {:?}", program); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + genesis_config + .native_instruction_processors + .push(solana_bpf_loader_program!()); + let bank = Arc::new(Bank::new(&genesis_config)); + let bank_client = BankClient::new_shared(&bank); + + let invoke_program_id = load_bpf_program(&bank_client, &mint_keypair, program.0); + let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1); + + let argument_keypair = Keypair::new(); + let account = Account::new(41, 100, &invoke_program_id); + bank.store_account(&argument_keypair.pubkey(), &account); + + let invoked_argument_keypair = Keypair::new(); + let account = Account::new(10, 10, &invoked_program_id); + bank.store_account(&invoked_argument_keypair.pubkey(), &account); + + let from_keypair = Keypair::new(); + let account = Account::new(43, 0, &solana_sdk::system_program::id()); + bank.store_account(&from_keypair.pubkey(), &account); + + let (derived_key1, nonce1) = + Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id); + let (derived_key2, nonce2) = + Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id); + let (derived_key3, nonce3) = + Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id); + + let mint_pubkey = mint_keypair.pubkey(); + let account_metas = vec![ + AccountMeta::new(mint_pubkey, true), + AccountMeta::new(argument_keypair.pubkey(), true), + AccountMeta::new_readonly(invoked_program_id, false), + AccountMeta::new(invoked_argument_keypair.pubkey(), true), + AccountMeta::new_readonly(invoked_program_id, false), + AccountMeta::new(argument_keypair.pubkey(), true), + AccountMeta::new(derived_key1, false), + AccountMeta::new(derived_key2, false), + AccountMeta::new_readonly(derived_key3, false), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new(from_keypair.pubkey(), true), + ]; + + // success cases + + let instruction = Instruction::new( + invoke_program_id, + &[TEST_SUCCESS, nonce1, nonce2, nonce3], + account_metas.clone(), + ); + let message = Message::new(&[instruction], Some(&mint_pubkey)); + assert!(bank_client .send_and_confirm_message( &[ &mint_keypair, @@ -439,9 +391,53 @@ fn test_program_bpf_invoke() { ], message, ) - .unwrap_err() - .unwrap(), - TransactionError::InstructionError(0, InstructionError::Custom(194969602)) - ); + .is_ok()); + + // failure cases + + let instruction = Instruction::new( + invoke_program_id, + &TEST_PRIVILEGE_ESCALATION_SIGNER, + account_metas.clone(), + ); + let message = Message::new(&[instruction], Some(&mint_pubkey)); + assert_eq!( + bank_client + .send_and_confirm_message( + &[ + &mint_keypair, + &argument_keypair, + &invoked_argument_keypair, + &from_keypair + ], + message, + ) + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(194969602)) + ); + + let instruction = Instruction::new( + invoke_program_id, + &TEST_PRIVILEGE_ESCALATION_WRITABLE, + account_metas.clone(), + ); + let message = Message::new(&[instruction], Some(&mint_pubkey)); + assert_eq!( + bank_client + .send_and_confirm_message( + &[ + &mint_keypair, + &argument_keypair, + &invoked_argument_keypair, + &from_keypair + ], + message, + ) + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::Custom(194969602)) + ); + } } } diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 535e34cb65400c..60ebef850803b5 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" [dependencies] bincode = "1.2.1" byteorder = "1.3.4" +jemalloc-sys = { version = "0.3.2", features = ["disable_initial_exec_tls"] } num-derive = { version = "0.3" } num-traits = { version = "0.2" } solana-runtime = { path = "../../runtime", version = "1.2.27" } @@ -23,7 +24,7 @@ rand = "0.7.3" rustversion = "1.0.3" [lib] -crate-type = ["lib"] +crate-type = ["lib", "cdylib"] name = "solana_bpf_loader_program" [package.metadata.docs.rs] diff --git a/programs/bpf_loader/src/deprecated.rs b/programs/bpf_loader/src/deprecated.rs index 3c1ee26602dda5..b2564c9ad1b54e 100644 --- a/programs/bpf_loader/src/deprecated.rs +++ b/programs/bpf_loader/src/deprecated.rs @@ -1,6 +1,9 @@ -solana_sdk::declare_builtin!( +use crate::process_instruction; + +solana_sdk::declare_loader!( solana_sdk::bpf_loader_deprecated::ID, solana_bpf_loader_deprecated_program, - solana_bpf_loader_program::process_instruction, + process_instruction, + solana_bpf_loader_program, deprecated::id ); diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index f39cbdb7a3b022..eb3d01ac2a13f9 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -29,10 +29,11 @@ use solana_sdk::{ }; use thiserror::Error; -solana_sdk::declare_builtin!( +solana_sdk::declare_loader!( solana_sdk::bpf_loader::ID, solana_bpf_loader_program, - solana_bpf_loader_program::process_instruction + process_instruction, + solana_bpf_loader_program ); #[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)] @@ -279,6 +280,15 @@ mod tests { } } + #[rustversion::since(1.46.0)] + #[test] + fn test_bpf_loader_same_crate() { + // Ensure that we can invoke this macro from the same crate + // where it is defined. + solana_bpf_loader_program!(); + solana_bpf_loader_deprecated_program!(); + } + #[test] #[should_panic(expected = "ExceededMaxInstructions(10)")] fn test_bpf_loader_non_terminating_program() { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 634a615c7e478b..29d575aeeda42b 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -10,7 +10,7 @@ use crate::{ accounts_db::{ErrorCounters, SnapshotStorages}, accounts_index::Ancestors, blockhash_queue::BlockhashQueue, - builtins::{get_builtins, get_epoch_activated_builtins}, + builtin_programs::{get_builtin_programs, get_epoch_activated_builtin_programs}, epoch_stakes::{EpochStakes, NodeVoteAccounts}, log_collector::LogCollector, message_processor::MessageProcessor, @@ -36,7 +36,7 @@ use solana_sdk::{ Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY, }, - entrypoint_native::{ProcessInstruction, ProcessInstructionWithContext}, + entrypoint_native::ProcessInstruction, epoch_info::EpochInfo, epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, @@ -96,26 +96,6 @@ type RentCollectionCycleParams = ( type EpochCount = u64; -#[derive(Copy, Clone)] -pub enum Entrypoint { - Program(ProcessInstruction), - Loader(ProcessInstructionWithContext), -} -pub struct Builtin { - pub name: String, - pub id: Pubkey, - pub entrypoint: Entrypoint, -} -impl Builtin { - pub fn new(name: &str, id: Pubkey, entrypoint: Entrypoint) -> Self { - Self { - name: name.to_string(), - id, - entrypoint, - } - } -} - #[derive(Default)] pub struct BankRc { /// where all the Accounts are stored @@ -483,9 +463,11 @@ impl Bank { entered_epoch_callback(&mut new) } - if let Some(builtins) = get_epoch_activated_builtins(new.operating_mode(), new.epoch) { - for program in builtins.iter() { - new.add_builtin(&program.name, program.id, program.entrypoint); + if let Some(builtin_programs) = + get_epoch_activated_builtin_programs(new.operating_mode(), new.epoch) + { + for program in builtin_programs.iter() { + new.add_builtin_program(&program.name, program.id, program.process_instruction); } } } @@ -2348,9 +2330,9 @@ impl Bank { } pub fn finish_init(&mut self) { - let builtins = get_builtins(self.operating_mode(), self.epoch); - for program in builtins.iter() { - self.add_builtin(&program.name, program.id, program.entrypoint); + let builtin_programs = get_builtin_programs(self.operating_mode(), self.epoch); + for program in builtin_programs.iter() { + self.add_builtin_program(&program.name, program.id, program.process_instruction); } } @@ -2802,36 +2784,19 @@ impl Bank { !self.is_delta.load(Ordering::Relaxed) } + /// Add an instruction processor to intercept instructions before the dynamic loader. pub fn add_builtin_program( &mut self, name: &str, program_id: Pubkey, process_instruction: ProcessInstruction, ) { - self.add_builtin(name, program_id, Entrypoint::Program(process_instruction)); - } - - pub fn add_builtin_loader( - &mut self, - name: &str, - program_id: Pubkey, - process_instruction_with_context: ProcessInstructionWithContext, - ) { - self.add_builtin( - name, - program_id, - Entrypoint::Loader(process_instruction_with_context), - ); - } - - /// Add an instruction processor to intercept instructions before the dynamic loader. - pub fn add_builtin(&mut self, name: &str, program_id: Pubkey, entrypoint: Entrypoint) { match self.get_account(&program_id) { Some(account) => { assert_eq!( account.owner, native_loader::id(), - "Cannot overwrite non-native account" + "Cannot overwrite non-native loader account" ); } None => { @@ -2840,18 +2805,9 @@ impl Bank { self.store_account(&program_id, &account); } } - match entrypoint { - Entrypoint::Program(process_instruction) => { - self.message_processor - .add_program(program_id, process_instruction); - debug!("Added builtin program {} under {:?}", name, program_id); - } - Entrypoint::Loader(process_instruction_with_context) => { - self.message_processor - .add_loader(program_id, process_instruction_with_context); - debug!("Added builtin loader {} under {:?}", name, program_id); - } - } + self.message_processor + .add_program(program_id, process_instruction); + debug!("Added static program {} under {:?}", name, program_id); } pub fn compare_bank(&self, dbank: &Bank) { @@ -2958,7 +2914,7 @@ mod tests { use super::*; use crate::{ accounts_index::{AccountMap, Ancestors}, - builtins::new_system_program_activation_epoch, + builtin_programs::new_system_program_activation_epoch, genesis_utils::{ create_genesis_config_with_leader, GenesisConfigInfo, BOOTSTRAP_VALIDATOR_LAMPORTS, }, diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs deleted file mode 100644 index 45e03ba43f0623..00000000000000 --- a/runtime/src/builtins.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{ - bank::{Builtin, Entrypoint}, - legacy_system_instruction_processor0, system_instruction_processor, -}; -use solana_sdk::{clock::Epoch, genesis_config::OperatingMode, system_program}; - -pub(crate) fn new_system_program_activation_epoch(operating_mode: OperatingMode) -> Epoch { - match operating_mode { - OperatingMode::Development => 0, - OperatingMode::Preview => 60, - OperatingMode::Stable => 40, - } -} - -/// All builtin programs that should be active at the given (operating_mode, epoch) -pub fn get_builtins(operating_mode: OperatingMode, epoch: Epoch) -> Vec { - vec![ - if epoch < new_system_program_activation_epoch(operating_mode) { - Builtin::new( - "system_program", - system_program::id(), - Entrypoint::Program(legacy_system_instruction_processor0::process_instruction), - ) - } else { - Builtin::new( - "system_program", - system_program::id(), - Entrypoint::Program(system_instruction_processor::process_instruction), - ) - }, - Builtin::new( - "config_program", - solana_config_program::id(), - Entrypoint::Program(solana_config_program::config_processor::process_instruction), - ), - Builtin::new( - "stake_program", - solana_stake_program::id(), - Entrypoint::Program(solana_stake_program::stake_instruction::process_instruction), - ), - Builtin::new( - "vote_program", - solana_vote_program::id(), - Entrypoint::Program(solana_vote_program::vote_instruction::process_instruction), - ), - ] -} - -/// Builtin programs that activate at the given (operating_mode, epoch) -pub fn get_epoch_activated_builtins( - operating_mode: OperatingMode, - epoch: Epoch, -) -> Option> { - if epoch == new_system_program_activation_epoch(operating_mode) { - Some(vec![Builtin::new( - "system_program", - system_program::id(), - Entrypoint::Program(system_instruction_processor::process_instruction), - )]) - } else { - None - } -} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ffd121f636cbf9..0b37a82a601a9d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -7,7 +7,7 @@ pub mod bank_client; pub mod bank_utils; mod blockhash_queue; pub mod bloom; -pub mod builtins; +pub mod builtin_programs; pub mod epoch_stakes; pub mod genesis_utils; mod legacy_system_instruction_processor0; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 910aaa6df8bf90..3f90e4dbefe56f 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use solana_sdk::{ account::{create_keyed_readonly_accounts, Account, KeyedAccount}, clock::Epoch, - entrypoint_native::{InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext}, + entrypoint_native::{InvokeContext, Logger, ProcessInstruction}, instruction::{CompiledInstruction, InstructionError}, message::Message, native_loader, @@ -247,6 +247,9 @@ impl Logger for ThisLogger { } } +pub type ProcessInstructionWithContext = + fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; + #[derive(Deserialize, Serialize)] pub struct MessageProcessor { #[serde(skip)] @@ -340,17 +343,6 @@ impl MessageProcessor { ) -> Result<(), InstructionError> { if native_loader::check_id(&keyed_accounts[0].owner()?) { let root_id = keyed_accounts[0].unsigned_key(); - for (id, process_instruction) in &self.loaders { - if id == root_id { - // Call the program via a builtin loader - return process_instruction( - &root_id, - &keyed_accounts[1..], - instruction_data, - invoke_context, - ); - } - } for (id, process_instruction) in &self.programs { if id == root_id { // Call the builtin program @@ -365,9 +357,9 @@ impl MessageProcessor { invoke_context, ); } else { - let owner_id = &keyed_accounts[0].owner()?; + let owner_id = keyed_accounts[0].owner()?; for (id, process_instruction) in &self.loaders { - if id == owner_id { + if *id == owner_id { // Call the program via a builtin loader return process_instruction( &owner_id, diff --git a/sdk/src/builtins.rs b/sdk/src/builtins.rs deleted file mode 100644 index 9df4fdd48287de..00000000000000 --- a/sdk/src/builtins.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! @brief Solana builtin helper macros - -#[rustversion::since(1.46.0)] -#[macro_export] -macro_rules! declare_builtin_name { - ($name:ident, $id:path, $entrypoint:expr) => { - #[macro_export] - macro_rules! $name { - () => { - // Subtle: - // The outer `declare_builtin_name!` macro may be expanded in another - // crate, causing the macro `$name!` to be defined in that - // crate. We want to emit a call to `$crate::id()`, and have - // `$crate` be resolved in the crate where `$name!` gets defined, - // *not* in this crate (where `declare_builtin_name! is defined). - // - // When a macro_rules! macro gets expanded, any $crate tokens - // in its output will be 'marked' with the crate they were expanded - // from. This includes nested macros like our macro `$name` - even - // though it looks like a separate macro, Rust considers it to be - // just another part of the output of `declare_program!`. - // - // We pass `$name` as the second argument to tell `respan!` to - // apply use the `Span` of `$name` when resolving `$crate::id`. - // This causes `$crate` to behave as though it was written - // at the same location as the `$name` value passed - // to `declare_builtin_name!` (e.g. the 'foo' in - // `declare_builtin_name(foo)` - // - // See the `respan!` macro for more details. - // This should use `crate::respan!` once - // https://github.com/rust-lang/rust/pull/72121 is merged: - // see https://github.com/solana-labs/solana/issues/10933. - // For now, we need to use `::solana_sdk` - // - // `respan!` respans the path `$crate::id`, which we then call (hence the extra - // parens) - ( - stringify!($name).to_string(), - ::solana_sdk::respan!($crate::$id, $name)(), - $entrypoint, - ) - }; - } - }; -} - -#[rustversion::not(since(1.46.0))] -#[macro_export] -macro_rules! declare_builtin_name { - ($name:ident, $id:path, $entrypoint:expr) => { - #[macro_export] - macro_rules! $name { - () => { - (stringify!($name).to_string(), $crate::$id(), $entrypoint) - }; - } - }; -} - -/// Convenience macro to declare a builtin -/// -/// bs58_string: bs58 string representation the program's id -/// name: Name of the program -/// entrypoint: Program's entrypoint, must be of `type Entrypoint` -/// id: Path to the program id access function, used if this macro is not -/// called in `src/lib` -/// -/// # Examples -/// -/// ``` -/// use std::str::FromStr; -/// // wrapper is used so that the macro invocation occurs in the item position -/// // rather than in the statement position which isn't allowed. -/// mod item_wrapper { -/// use solana_sdk::account::KeyedAccount; -/// use solana_sdk::instruction::InstructionError; -/// use solana_sdk::pubkey::Pubkey; -/// use solana_sdk::declare_builtin; -/// -/// fn my_process_instruction( -/// program_id: &Pubkey, -/// keyed_accounts: &[KeyedAccount], -/// instruction_data: &[u8], -/// ) -> Result<(), InstructionError> { -/// // Process an instruction -/// Ok(()) -/// } -/// -/// declare_builtin!( -/// "My11111111111111111111111111111111111111111", -/// solana_my_program, -/// my_process_instruction -/// ); -/// -/// # } -/// # use solana_sdk::pubkey::Pubkey; -/// # use item_wrapper::id; -/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); -/// assert_eq!(id(), my_id); -/// ``` -/// ``` -/// use std::str::FromStr; -/// # // wrapper is used so that the macro invocation occurs in the item position -/// # // rather than in the statement position which isn't allowed. -/// # mod item_wrapper { -/// use solana_sdk::account::KeyedAccount; -/// use solana_sdk::instruction::InstructionError; -/// use solana_sdk::pubkey::Pubkey; -/// use solana_sdk::declare_builtin; -/// -/// fn my_process_instruction( -/// program_id: &Pubkey, -/// keyed_accounts: &[KeyedAccount], -/// instruction_data: &[u8], -/// ) -> Result<(), InstructionError> { -/// // Process an instruction -/// Ok(()) -/// } -/// -/// declare_builtin!( -/// solana_sdk::system_program::ID, -/// solana_my_program, -/// my_process_instruction -/// ); -/// } -/// -/// # use item_wrapper::id; -/// assert_eq!(id(), solana_sdk::system_program::ID); -/// ``` -#[macro_export] -macro_rules! declare_builtin { - ($bs58_string:expr, $name:ident, $entrypoint:expr) => { - $crate::declare_builtin!($bs58_string, $name, $entrypoint, id); - }; - ($bs58_string:expr, $name:ident, $entrypoint:expr, $id:path) => { - $crate::declare_id!($bs58_string); - $crate::declare_builtin_name!($name, $id, $entrypoint); - }; -} diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index 277940d5755a84..9992f040d497d1 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -93,8 +93,7 @@ macro_rules! declare_name { /// name: Name of the program /// filename: must match the library name in Cargo.toml /// entrypoint: Program's entrypoint, must be of `type Entrypoint` -/// id: Path to the program id access function, used if this macro is not -/// called in `src/lib` +/// id: Path to the program id access function, used if not called in `src/lib` /// /// # Examples /// @@ -175,9 +174,32 @@ macro_rules! declare_program( ) ); +/// Same as declare_program but for native loaders +#[macro_export] +macro_rules! declare_loader { + ($bs58_string:expr, $name:ident, $entrypoint:expr) => { + $crate::declare_loader!($bs58_string, $name, $entrypoint, $name, id); + }; + ($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident) => { + $crate::declare_loader!($bs58_string, $name, $entrypoint, $filename, id); + }; + ($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident, $id:path) => { + $crate::declare_id!($bs58_string); + $crate::declare_name!($name, $filename, $id); + + #[no_mangle] + pub extern "C" fn $name( + program_id: &$crate::pubkey::Pubkey, + keyed_accounts: &[$crate::account::KeyedAccount], + instruction_data: &[u8], + invoke_context: &mut dyn $crate::entrypoint_native::InvokeContext, + ) -> Result<(), $crate::instruction::InstructionError> { + $entrypoint(program_id, keyed_accounts, instruction_data, invoke_context) + } + }; +} + pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>; -pub type ProcessInstructionWithContext = - fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; /// Cross-program invocation context passed to loaders pub trait InvokeContext { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 525c8295764f19..68d791710b623d 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -7,7 +7,6 @@ pub mod account; pub mod account_utils; pub mod bpf_loader; pub mod bpf_loader_deprecated; -pub mod builtins; pub mod clock; pub mod commitment_config; pub mod decode_error;