diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index dc598c6f47..7ce836eefb 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2708,6 +2708,13 @@ dependencies = [ "solana-program 1.6.0", ] +[[package]] +name = "solana-bpf-rust-finalize" +version = "1.6.0" +dependencies = [ + "solana-program 1.6.0", +] + [[package]] name = "solana-bpf-rust-instruction-introspection" version = "1.6.0" diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index 4ce28d7064..8238f3098a 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -51,6 +51,7 @@ members = [ "rust/dup_accounts", "rust/error_handling", "rust/external_spend", + "rust/finalize", "rust/instruction_introspection", "rust/invoke", "rust/invoke_and_error", diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index ae86236f30..340a38b0c2 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -175,7 +175,8 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { let mint_pubkey = mint_keypair.pubkey(); let account_metas = vec![AccountMeta::new(mint_pubkey, true)]; - let instruction = Instruction::new_with_bincode(invoke_program_id, &[u8::MAX, 0, 0, 0], account_metas); + let instruction = + Instruction::new_with_bincode(invoke_program_id, &[u8::MAX, 0, 0, 0], account_metas); let message = Message::new(&[instruction], Some(&mint_pubkey)); bank_client diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index f7fa029c80..79bb1ec3c9 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -68,6 +68,7 @@ fn main() { "dup_accounts", "error_handling", "external_spend", + "finalize", "instruction_introspection", "invoke", "invoke_and_error", diff --git a/programs/bpf/rust/finalize/Cargo.toml b/programs/bpf/rust/finalize/Cargo.toml new file mode 100644 index 0000000000..a0a64879fb --- /dev/null +++ b/programs/bpf/rust/finalize/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "solana-bpf-rust-finalize" +version = "1.6.0" +description = "Solana BPF test program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +edition = "2018" + +[dependencies] +solana-program = { path = "../../../../sdk/program", version = "1.6.0" } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/finalize/Xargo.toml b/programs/bpf/rust/finalize/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/programs/bpf/rust/finalize/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/programs/bpf/rust/finalize/src/lib.rs b/programs/bpf/rust/finalize/src/lib.rs new file mode 100644 index 0000000000..50e947afdd --- /dev/null +++ b/programs/bpf/rust/finalize/src/lib.rs @@ -0,0 +1,25 @@ +//! @brief Example Rust-based BPF sanity program that finalizes a BPF program + +#![allow(unreachable_code)] + +extern crate solana_program; +use solana_program::{ + account_info::AccountInfo, bpf_loader, entrypoint, entrypoint::ProgramResult, + loader_instruction, msg, program::invoke, pubkey::Pubkey, +}; + +entrypoint!(process_instruction); +fn process_instruction( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { + msg!("Finalize a program"); + invoke( + &loader_instruction::finalize(&accounts[0].key.clone(), &bpf_loader::id()), + accounts, + )?; + msg!("check executable"); + assert!(accounts[0].executable); + Ok(()) +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 404abacf77..321d519b3a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -879,8 +879,11 @@ fn test_program_bpf_invoke_sanity() { &invoked_argument_keypair, &from_keypair, ]; - let instruction = - Instruction::new_with_bytes(invoke_program_id, instruction_data, account_metas.clone()); + let instruction = Instruction::new_with_bytes( + invoke_program_id, + instruction_data, + account_metas.clone(), + ); let message = Message::new(&[instruction], Some(&mint_pubkey)); let tx = Transaction::new(&signers, message.clone(), bank.last_blockhash()); let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); @@ -1057,7 +1060,8 @@ fn test_program_bpf_program_id_spoofing() { AccountMeta::new(to_pubkey, false), ]; - let instruction = Instruction::new_with_bytes(malicious_swap_pubkey, &[], account_metas.clone()); + let instruction = + Instruction::new_with_bytes(malicious_swap_pubkey, &[], account_metas.clone()); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), @@ -1401,7 +1405,11 @@ fn test_program_bpf_test_use_latest_executor() { // Call the noop program, should get noop not panic let message = Message::new( - &[Instruction::new_with_bytes(program_keypair.pubkey(), &[0], vec![])], + &[Instruction::new_with_bytes( + program_keypair.pubkey(), + &[0], + vec![], + )], Some(&mint_keypair.pubkey()), ); assert!(bank_client @@ -1491,7 +1499,11 @@ fn test_program_bpf_test_use_latest_executor2() { // invoke program, verify not found let message = Message::new( - &[Instruction::new_with_bytes(program_keypair.pubkey(), &[0], vec![])], + &[Instruction::new_with_bytes( + program_keypair.pubkey(), + &[0], + vec![], + )], Some(&mint_keypair.pubkey()), ); assert_eq!( @@ -1526,7 +1538,11 @@ fn test_program_bpf_test_use_latest_executor2() { // Call the program, should get noop, not panic let message = Message::new( - &[Instruction::new_with_bytes(program_keypair.pubkey(), &[0], vec![])], + &[Instruction::new_with_bytes( + program_keypair.pubkey(), + &[0], + vec![], + )], Some(&mint_keypair.pubkey()), ); assert!(bank_client @@ -1848,7 +1864,8 @@ fn test_program_bpf_disguised_as_bpf_loader() { program, ); let account_metas = vec![AccountMeta::new_readonly(program_id, false)]; - let instruction = Instruction::new_with_bytes(bpf_loader_deprecated::id(), &[1], account_metas); + let instruction = + Instruction::new_with_bytes(bpf_loader_deprecated::id(), &[1], account_metas); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), @@ -2222,3 +2239,65 @@ fn test_program_bpf_syscall_feature_activation() { println!("result: {:?}", result); assert!(result.is_ok()); } + +#[cfg(feature = "bpf_rust")] +#[test] +fn test_program_bpf_finalize() { + 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(&name, id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); + + let program_pubkey = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_finalize", + ); + + let noop_keypair = Keypair::new(); + + // Write the noop program into the same program account + let elf = read_bpf_program("solana_bpf_rust_noop"); + let message = Message::new( + &[system_instruction::create_account( + &mint_keypair.pubkey(), + &noop_keypair.pubkey(), + 1, + elf.len() as u64 * 2, + &bpf_loader::id(), + )], + Some(&mint_keypair.pubkey()), + ); + assert!(bank_client + .send_and_confirm_message(&[&mint_keypair, &noop_keypair], message) + .is_ok()); + write_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + &noop_keypair, + &elf, + ); + + let account_metas = vec![ + AccountMeta::new(noop_keypair.pubkey(), true), + AccountMeta::new_readonly(bpf_loader::id(), false), + AccountMeta::new(rent::id(), false), + ]; + let instruction = Instruction::new_with_bytes(program_pubkey, &[], account_metas.clone()); + let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); + let result = bank_client.send_and_confirm_message(&[&mint_keypair, &noop_keypair], message); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) + ); +}