Make BPF Loader static (#11516)

This commit is contained in:
Jack May 2020-08-14 12:32:45 -07:00 committed by GitHub
parent 346e982e28
commit 7c736f71fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 756 additions and 577 deletions

2
Cargo.lock generated
View File

@ -3378,7 +3378,6 @@ version = "1.4.0"
dependencies = [
"bincode",
"byteorder",
"jemalloc-sys",
"num-derive 0.3.0",
"num-traits",
"rand 0.7.3",
@ -3556,7 +3555,6 @@ dependencies = [
"serial_test_derive",
"solana-account-decoder",
"solana-banks-server",
"solana-bpf-loader-program",
"solana-clap-utils",
"solana-client",
"solana-faucet",

View File

@ -45,7 +45,6 @@ serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
solana-banks-server = { path = "../banks-server", version = "1.4.0" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.0" }
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
solana-client = { path = "../client", version = "1.4.0" }
solana-faucet = { path = "../faucet", version = "1.4.0" }

View File

@ -72,9 +72,6 @@ pub mod vote_stake_tracker;
pub mod weighted_shuffle;
pub mod window_service;
#[macro_use]
extern crate solana_bpf_loader_program;
#[macro_use]
extern crate log;

View File

@ -868,13 +868,6 @@ impl TestValidator {
42,
bootstrap_validator_lamports,
);
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);

View File

@ -1,7 +1,3 @@
use solana_sdk::{
clock::Epoch, genesis_config::OperatingMode, inflation::Inflation, pubkey::Pubkey,
};
#[macro_use]
extern crate solana_bpf_loader_program;
#[macro_use]
@ -13,6 +9,10 @@ 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<Inflation> {
match operating_mode {
@ -44,22 +44,27 @@ pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option<Infl
}
}
pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(String, Pubkey)>> {
enum Program {
Native((String, Pubkey)),
BuiltinLoader((String, Pubkey, ProcessInstructionWithContext)),
}
fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<Program>> {
match operating_mode {
OperatingMode::Development => {
if epoch == 0 {
// Programs used for testing
Some(vec![
solana_bpf_loader_program!(),
solana_bpf_loader_deprecated_program!(),
solana_vest_program!(),
solana_budget_program!(),
solana_exchange_program!(),
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!()),
])
} 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![solana_bpf_loader_program!()])
Some(vec![Program::BuiltinLoader(solana_bpf_loader_program!())])
} else {
None
}
@ -68,7 +73,10 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
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![solana_bpf_loader_program!(), solana_vest_program!()])
Some(vec![
Program::BuiltinLoader(solana_bpf_loader_program!()),
Program::Native(solana_vest_program!()),
])
} else {
None
}
@ -77,7 +85,10 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
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![solana_bpf_loader_program!(), solana_vest_program!()])
Some(vec![
Program::BuiltinLoader(solana_bpf_loader_program!()),
Program::Native(solana_vest_program!()),
])
} else {
None
}
@ -85,21 +96,48 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
}
}
pub fn get_native_programs(
operating_mode: OperatingMode,
epoch: Epoch,
) -> Option<Vec<(String, Pubkey)>> {
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(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 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 OperatingMode::Stable == operating_mode {
@ -118,8 +156,13 @@ mod tests {
#[test]
fn test_id_uniqueness() {
let mut unique = HashSet::new();
let ids = get_programs(OperatingMode::Development, 0).unwrap();
assert!(ids.into_iter().all(move |id| unique.insert(id)));
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))),
}
}
}
#[test]
@ -137,7 +180,18 @@ mod tests {
get_programs(OperatingMode::Development, 0).unwrap().len(),
5
);
assert_eq!(get_programs(OperatingMode::Development, 1), None);
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());
}
#[test]
@ -155,7 +209,7 @@ mod tests {
#[test]
fn test_softlaunch_programs() {
assert_eq!(get_programs(OperatingMode::Stable, 1), None);
assert!(get_programs(OperatingMode::Stable, 1).is_none());
assert!(get_programs(OperatingMode::Stable, std::u64::MAX).is_some());
}
}

View File

@ -468,7 +468,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
);
let native_instruction_processors =
solana_genesis_programs::get_programs(operating_mode, 0).unwrap_or_else(Vec::new);
solana_genesis_programs::get_native_programs(operating_mode, 0).unwrap_or_else(Vec::new);
let inflation = solana_genesis_programs::get_inflation(operating_mode, 0).unwrap();
let mut genesis_config = GenesisConfig {

View File

@ -323,11 +323,11 @@ pub fn process_blockstore(
}
// Setup bank for slot 0
let bank0 = Arc::new(Bank::new_with_paths(
&genesis_config,
account_paths,
&opts.frozen_accounts,
));
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);
info!("processing ledger for slot 0...");
let recyclers = VerifyRecyclers::default();
process_bank_0(&bank0, blockstore, &opts, &recyclers)?;

View File

@ -171,7 +171,7 @@ impl LocalCluster {
match genesis_config.operating_mode {
OperatingMode::Stable | OperatingMode::Preview => {
genesis_config.native_instruction_processors =
solana_genesis_programs::get_programs(genesis_config.operating_mode, 0)
solana_genesis_programs::get_native_programs(genesis_config.operating_mode, 0)
.unwrap_or_default()
}
_ => (),

View File

@ -1616,7 +1616,6 @@ version = "1.4.0"
dependencies = [
"bincode",
"byteorder 1.3.4",
"jemalloc-sys",
"num-derive 0.3.0",
"num-traits",
"solana-runtime",

View File

@ -1,394 +1,413 @@
#![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"))]
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};
fn test_program_bpf_sanity() {
solana_logger::setup();
/// 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
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),
]);
}
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);
#[test]
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
fn test_program_bpf_sanity() {
solana_logger::setup();
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 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),
]);
}
// 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);
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());
}
// 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")]);
}
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);
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);
}
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")]);
}
#[test]
fn test_program_bpf_error_handling() {
solana_logger::setup();
for program in programs.iter() {
println!("Test program: {:?}", program);
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")]);
}
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);
for program in programs.iter() {
println!("Test program: {:?}", program);
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),
];
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)];
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);
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());
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 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)
);
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);
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))
);
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);
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))
);
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);
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)
);
}
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);
}
}
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)
);
}
#[test]
fn test_program_bpf_error_handling() {
solana_logger::setup();
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)
);
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::MaxSeedLengthExceeded)
);
}
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")]);
}
#[test]
fn test_program_bpf_invoke() {
solana_logger::setup();
for program in programs.iter() {
println!("Test program: {:?}", program);
const TEST_SUCCESS: u8 = 1;
const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2;
const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3;
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 mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
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
{
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 {
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(),
assert_eq!(
result,
TransactionError::InstructionError(0, InstructionError::InvalidError)
);
let message = Message::new(&[instruction], Some(&mint_pubkey));
assert!(bank_client
}
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)
);
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::MaxSeedLengthExceeded)
);
}
}
#[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;
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,
@ -398,53 +417,31 @@ mod bpf {
],
message,
)
.is_ok());
.unwrap_err()
.unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
);
// 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))
);
}
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))
);
}
}

View File

@ -11,7 +11,6 @@ edition = "2018"
[dependencies]
bincode = "1.3.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.4.0" }
@ -24,7 +23,7 @@ rand = "0.7.3"
rustversion = "1.0.3"
[lib]
crate-type = ["lib", "cdylib"]
crate-type = ["lib"]
name = "solana_bpf_loader_program"
[package.metadata.docs.rs]

View File

@ -1,9 +1,6 @@
use crate::process_instruction;
solana_sdk::declare_loader!(
solana_sdk::declare_builtin!(
solana_sdk::bpf_loader_deprecated::ID,
solana_bpf_loader_deprecated_program,
process_instruction,
solana_bpf_loader_program,
solana_bpf_loader_program::process_instruction,
deprecated::id
);

View File

@ -29,11 +29,10 @@ use solana_sdk::{
};
use thiserror::Error;
solana_sdk::declare_loader!(
solana_sdk::declare_builtin!(
solana_sdk::bpf_loader::ID,
solana_bpf_loader_program,
process_instruction,
solana_bpf_loader_program
solana_bpf_loader_program::process_instruction
);
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
@ -280,15 +279,6 @@ 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() {

View File

@ -10,7 +10,7 @@ use crate::{
accounts_db::{ErrorCounters, SnapshotStorages},
accounts_index::Ancestors,
blockhash_queue::BlockhashQueue,
builtin_programs::{get_builtin_programs, get_epoch_activated_builtin_programs},
builtins::{get_builtins, get_epoch_activated_builtins},
epoch_stakes::{EpochStakes, NodeVoteAccounts},
log_collector::LogCollector,
message_processor::MessageProcessor,
@ -35,7 +35,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,
entrypoint_native::{ProcessInstruction, ProcessInstructionWithContext},
epoch_info::EpochInfo,
epoch_schedule::EpochSchedule,
fee_calculator::{FeeCalculator, FeeRateGovernor},
@ -98,6 +98,26 @@ 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
@ -544,11 +564,9 @@ impl Bank {
entered_epoch_callback(&mut new)
}
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);
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);
}
}
}
@ -2575,9 +2593,9 @@ impl Bank {
}
pub fn finish_init(&mut self) {
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);
let builtins = get_builtins();
for program in builtins.iter() {
self.add_builtin(&program.name, program.id, program.entrypoint);
}
}
@ -3021,19 +3039,36 @@ 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 loader account"
"Cannot overwrite non-native account"
);
}
None => {
@ -3042,9 +3077,18 @@ impl Bank {
self.store_account(&program_id, &account);
}
}
self.message_processor
.add_program(program_id, process_instruction);
debug!("Added static program {} under {:?}", name, program_id);
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);
}
}
}
pub fn compare_bank(&self, dbank: &Bank) {

View File

@ -1,54 +0,0 @@
use crate::system_instruction_processor;
use solana_sdk::{
clock::Epoch, entrypoint_native::ProcessInstruction, genesis_config::OperatingMode,
pubkey::Pubkey, system_program,
};
pub struct BuiltinProgram {
pub name: String,
pub id: Pubkey,
pub process_instruction: ProcessInstruction,
}
impl BuiltinProgram {
pub fn new(name: &str, id: Pubkey, process_instruction: ProcessInstruction) -> Self {
Self {
name: name.to_string(),
id,
process_instruction,
}
}
}
/// All builtin programs that should be active at the given (operating_mode, epoch)
pub fn get_builtin_programs(_operating_mode: OperatingMode, _epoch: Epoch) -> Vec<BuiltinProgram> {
vec![
BuiltinProgram::new(
"system_program",
system_program::id(),
system_instruction_processor::process_instruction,
),
BuiltinProgram::new(
"config_program",
solana_config_program::id(),
solana_config_program::config_processor::process_instruction,
),
BuiltinProgram::new(
"stake_program",
solana_stake_program::id(),
solana_stake_program::stake_instruction::process_instruction,
),
BuiltinProgram::new(
"vote_program",
solana_vote_program::id(),
solana_vote_program::vote_instruction::process_instruction,
),
]
}
/// Builtin programs that activate at the given (operating_mode, epoch)
pub fn get_epoch_activated_builtin_programs(
_operating_mode: OperatingMode,
_epoch: Epoch,
) -> Option<Vec<BuiltinProgram>> {
None
}

39
runtime/src/builtins.rs Normal file
View File

@ -0,0 +1,39 @@
use crate::{
bank::{Builtin, Entrypoint},
system_instruction_processor,
};
use solana_sdk::{clock::Epoch, genesis_config::OperatingMode, system_program};
/// All builtin programs that should be active at the given (operating_mode, epoch)
pub fn get_builtins() -> Vec<Builtin> {
vec![
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<Vec<Builtin>> {
None
}

View File

@ -9,7 +9,7 @@ pub mod bank_forks;
pub mod bank_utils;
mod blockhash_queue;
pub mod bloom;
pub mod builtin_programs;
pub mod builtins;
pub mod commitment;
pub mod epoch_stakes;
pub mod genesis_utils;

View File

@ -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},
entrypoint_native::{InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext},
instruction::{CompiledInstruction, InstructionError},
message::Message,
native_loader,
@ -247,9 +247,6 @@ impl Logger for ThisLogger {
}
}
pub type ProcessInstructionWithContext =
fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>;
#[derive(Deserialize, Serialize)]
pub struct MessageProcessor {
#[serde(skip)]
@ -353,6 +350,17 @@ 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
@ -367,9 +375,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,

140
sdk/src/builtins.rs Normal file
View File

@ -0,0 +1,140 @@
//! @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);
};
}

View File

@ -93,7 +93,8 @@ 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 not called in `src/lib`
/// id: Path to the program id access function, used if this macro is not
/// called in `src/lib`
///
/// # Examples
///
@ -174,32 +175,9 @@ 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>;
/// Invocation context passed to loaders
pub trait InvokeContext {

View File

@ -13,6 +13,7 @@ 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;