From dc217e902498f8833dd08871dcab686243f9b9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 27 Oct 2021 23:38:44 +0200 Subject: [PATCH 1/9] Replaces MockInvokeContext by ThisInvokeContext in BpfLoader. --- programs/bpf/benches/bpf_loader.rs | 349 ++++++------- programs/bpf/tests/programs.rs | 198 ++++---- programs/bpf_loader/src/lib.rs | 742 +++++++++++++++------------- programs/bpf_loader/src/syscalls.rs | 219 ++++---- runtime/src/message_processor.rs | 39 ++ 5 files changed, 792 insertions(+), 755 deletions(-) diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 26d6100eed7dd8..68d3c25128d395 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -17,19 +17,19 @@ use solana_runtime::{ bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_program, + message_processor::with_mock_invoke_context, }; use solana_sdk::{ - account::AccountSharedData, bpf_loader, client::SyncClient, entrypoint::SUCCESS, instruction::{AccountMeta, Instruction}, message::Message, - process_instruction::{InvokeContext, MockComputeMeter, MockInvokeContext}, + process_instruction::InvokeContext, pubkey::Pubkey, signature::{Keypair, Signer}, }; -use std::{cell::RefCell, env, fs::File, io::Read, mem, path::PathBuf, rc::Rc, sync::Arc}; +use std::{env, fs::File, io::Read, mem, path::PathBuf, sync::Arc}; use test::Bencher; /// BPF program file extension @@ -94,93 +94,73 @@ fn bench_program_alu(bencher: &mut Bencher) { .write_u64::(ARMSTRONG_LIMIT) .unwrap(); inner_iter.write_u64::(0).unwrap(); - - let loader_id = bpf_loader::id(); - let program_id = solana_sdk::pubkey::new_rand(); - let accounts = [ - ( - program_id, - RefCell::new(AccountSharedData::new(0, 0, &loader_id)), - ), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new( - 1, - 10000001, - &solana_sdk::pubkey::new_rand(), - )), - ), - ]; - let keyed_accounts: Vec<_> = accounts - .iter() - .map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account)) - .collect(); - let mut invoke_context = MockInvokeContext::new(&program_id, keyed_accounts); - let elf = load_elf("bench_alu").unwrap(); - let mut executable = >::from_elf( - &elf, - None, - Config::default(), - register_syscalls(&mut invoke_context).unwrap(), - ) - .unwrap(); - executable.jit_compile().unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; - let mut vm = create_vm( - &loader_id, - executable.as_ref(), - &mut inner_iter, - &mut invoke_context, - &[], - ) - .unwrap(); - - println!("Interpreted:"); - assert_eq!( - SUCCESS, - vm.execute_program_interpreted(&mut instruction_meter) - .unwrap() - ); - assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); - assert_eq!( - ARMSTRONG_EXPECTED, - LittleEndian::read_u64(&inner_iter[mem::size_of::()..]) - ); + let loader_id = bpf_loader::id(); + with_mock_invoke_context(loader_id, 10000001, |invoke_context| { + let mut executable = >::from_elf( + &elf, + None, + Config::default(), + register_syscalls(invoke_context).unwrap(), + ) + .unwrap(); + executable.jit_compile().unwrap(); + let compute_meter = invoke_context.get_compute_meter(); + let mut instruction_meter = ThisInstructionMeter { compute_meter }; + let mut vm = create_vm( + &loader_id, + executable.as_ref(), + &mut inner_iter, + invoke_context, + &[], + ) + .unwrap(); - bencher.iter(|| { - vm.execute_program_interpreted(&mut instruction_meter) - .unwrap(); + println!("Interpreted:"); + assert_eq!( + SUCCESS, + vm.execute_program_interpreted(&mut instruction_meter) + .unwrap() + ); + assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); + assert_eq!( + ARMSTRONG_EXPECTED, + LittleEndian::read_u64(&inner_iter[mem::size_of::()..]) + ); + + bencher.iter(|| { + vm.execute_program_interpreted(&mut instruction_meter) + .unwrap(); + }); + let instructions = vm.get_total_instruction_count(); + let summary = bencher.bench(|_bencher| {}).unwrap(); + println!(" {:?} instructions", instructions); + println!(" {:?} ns/iter median", summary.median as u64); + assert!(0f64 != summary.median); + let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million; + println!(" {:?} MIPS", mips); + println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips); + + println!("JIT to native:"); + assert_eq!( + SUCCESS, + vm.execute_program_jit(&mut instruction_meter).unwrap() + ); + assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); + assert_eq!( + ARMSTRONG_EXPECTED, + LittleEndian::read_u64(&inner_iter[mem::size_of::()..]) + ); + + bencher.iter(|| vm.execute_program_jit(&mut instruction_meter).unwrap()); + let summary = bencher.bench(|_bencher| {}).unwrap(); + println!(" {:?} instructions", instructions); + println!(" {:?} ns/iter median", summary.median as u64); + assert!(0f64 != summary.median); + let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million; + println!(" {:?} MIPS", mips); + println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_jit_to_native_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips); }); - let instructions = vm.get_total_instruction_count(); - let summary = bencher.bench(|_bencher| {}).unwrap(); - println!(" {:?} instructions", instructions); - println!(" {:?} ns/iter median", summary.median as u64); - assert!(0f64 != summary.median); - let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million; - println!(" {:?} MIPS", mips); - println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips); - - println!("JIT to native:"); - assert_eq!( - SUCCESS, - vm.execute_program_jit(&mut instruction_meter).unwrap() - ); - assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); - assert_eq!( - ARMSTRONG_EXPECTED, - LittleEndian::read_u64(&inner_iter[mem::size_of::()..]) - ); - - bencher.iter(|| vm.execute_program_jit(&mut instruction_meter).unwrap()); - let summary = bencher.bench(|_bencher| {}).unwrap(); - println!(" {:?} instructions", instructions); - println!(" {:?} ns/iter median", summary.median as u64); - assert!(0f64 != summary.median); - let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million; - println!(" {:?} MIPS", mips); - println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_jit_to_native_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips); } #[bench] @@ -220,125 +200,102 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { #[bench] fn bench_create_vm(bencher: &mut Bencher) { - const BUDGET: u64 = 200_000; - let loader_id = bpf_loader::id(); - - let accounts = [RefCell::new(AccountSharedData::new( - 1, - 10000001, - &solana_sdk::pubkey::new_rand(), - ))]; - let keys = [solana_sdk::pubkey::new_rand()]; - let keyed_accounts: Vec<_> = keys - .iter() - .zip(&accounts) - .map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account)) - .collect(); - let instruction_data = vec![0u8]; - - let mut invoke_context = MockInvokeContext::new(&loader_id, keyed_accounts); - invoke_context.compute_meter = Rc::new(RefCell::new(MockComputeMeter { remaining: BUDGET })); - - // Serialize account data - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - let (mut serialized, account_lengths) = serialize_parameters( - &loader_id, - &solana_sdk::pubkey::new_rand(), - keyed_accounts, - &instruction_data, - ) - .unwrap(); - let elf = load_elf("noop").unwrap(); - let executable = >::from_elf( - &elf, - None, - Config::default(), - register_syscalls(&mut invoke_context).unwrap(), - ) - .unwrap(); + let loader_id = bpf_loader::id(); + with_mock_invoke_context(loader_id, 10000001, |invoke_context| { + const BUDGET: u64 = 200_000; + let compute_meter = invoke_context.get_compute_meter(); + { + let mut compute_meter = compute_meter.borrow_mut(); + let to_consume = compute_meter.get_remaining() - BUDGET; + compute_meter.consume(to_consume).unwrap(); + } + + // Serialize account data + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); + let (mut serialized, account_lengths) = serialize_parameters( + &keyed_accounts[0].unsigned_key(), + &keyed_accounts[1].unsigned_key(), + &keyed_accounts[2..], + &[], + ) + .unwrap(); - bencher.iter(|| { - let _ = create_vm( - &loader_id, - executable.as_ref(), - serialized.as_slice_mut(), - &mut invoke_context, - &account_lengths, + let executable = >::from_elf( + &elf, + None, + Config::default(), + register_syscalls(invoke_context).unwrap(), ) .unwrap(); + + bencher.iter(|| { + let _ = create_vm( + &loader_id, + executable.as_ref(), + serialized.as_slice_mut(), + invoke_context, + &account_lengths, + ) + .unwrap(); + }); }); } #[bench] fn bench_instruction_count_tuner(_bencher: &mut Bencher) { - const BUDGET: u64 = 200_000; - let loader_id = bpf_loader::id(); - let program_id = solana_sdk::pubkey::new_rand(); - - let accounts = [ - ( - program_id, - RefCell::new(AccountSharedData::new(0, 0, &loader_id)), - ), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new( - 1, - 10000001, - &solana_sdk::pubkey::new_rand(), - )), - ), - ]; - let keyed_accounts: Vec<_> = accounts - .iter() - .map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account)) - .collect(); - let instruction_data = vec![0u8]; - let mut invoke_context = MockInvokeContext::new(&program_id, keyed_accounts); - invoke_context.compute_meter = Rc::new(RefCell::new(MockComputeMeter { remaining: BUDGET })); - - // Serialize account data - let (mut serialized, account_lengths) = serialize_parameters( - &loader_id, - &program_id, - &invoke_context.get_keyed_accounts().unwrap()[1..], - &instruction_data, - ) - .unwrap(); - let elf = load_elf("tuner").unwrap(); - let executable = >::from_elf( - &elf, - None, - Config::default(), - register_syscalls(&mut invoke_context).unwrap(), - ) - .unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; - let mut vm = create_vm( - &loader_id, - executable.as_ref(), - serialized.as_slice_mut(), - &mut invoke_context, - &account_lengths, - ) - .unwrap(); + let loader_id = bpf_loader::id(); + with_mock_invoke_context(loader_id, 10000001, |invoke_context| { + const BUDGET: u64 = 200_000; + let compute_meter = invoke_context.get_compute_meter(); + { + let mut compute_meter = compute_meter.borrow_mut(); + let to_consume = compute_meter.get_remaining() - BUDGET; + compute_meter.consume(to_consume).unwrap(); + } + + // Serialize account data + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); + let (mut serialized, account_lengths) = serialize_parameters( + &keyed_accounts[0].unsigned_key(), + &keyed_accounts[1].unsigned_key(), + &keyed_accounts[2..], + &[], + ) + .unwrap(); - let mut measure = Measure::start("tune"); - let _ = vm.execute_program_interpreted(&mut instruction_meter); - measure.stop(); + let executable = >::from_elf( + &elf, + None, + Config::default(), + register_syscalls(invoke_context).unwrap(), + ) + .unwrap(); + let mut instruction_meter = ThisInstructionMeter { compute_meter }; + let mut vm = create_vm( + &loader_id, + executable.as_ref(), + serialized.as_slice_mut(), + invoke_context, + &account_lengths, + ) + .unwrap(); - assert_eq!( - 0, - instruction_meter.get_remaining(), - "Tuner must consume the whole budget" - ); - println!( - "{:?} compute units took {:?} us ({:?} instructions)", - BUDGET - instruction_meter.get_remaining(), - measure.as_us(), - vm.get_total_instruction_count(), - ); + let mut measure = Measure::start("tune"); + let _ = vm.execute_program_interpreted(&mut instruction_meter); + measure.stop(); + + assert_eq!( + 0, + instruction_meter.get_remaining(), + "Tuner must consume the whole budget" + ); + println!( + "{:?} compute units took {:?} us ({:?} instructions)", + BUDGET - instruction_meter.get_remaining(), + measure.as_us(), + vm.get_total_instruction_count(), + ); + }); } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index b5fb410e5221d7..01e3edc5bb3b35 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -30,6 +30,7 @@ use solana_runtime::{ load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority, upgrade_program, }, + message_processor::with_mock_invoke_context, }; use solana_sdk::{ account::{AccountSharedData, ReadableAccount}, @@ -40,10 +41,9 @@ use solana_sdk::{ compute_budget::{ComputeBudget, ComputeBudgetInstruction}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, - keyed_account::KeyedAccount, loader_instruction, message::{Message, SanitizedMessage}, - process_instruction::{InvokeContext, MockInvokeContext}, + process_instruction::InvokeContext, pubkey::Pubkey, signature::{keypair_from_seed, Keypair, Signer}, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, @@ -56,8 +56,8 @@ use solana_transaction_status::{ TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding, }; use std::{ - cell::RefCell, collections::HashMap, convert::TryFrom, env, fs::File, io::Read, path::PathBuf, - str::FromStr, sync::Arc, + collections::HashMap, convert::TryFrom, env, fs::File, io::Read, path::PathBuf, str::FromStr, + sync::Arc, }; /// BPF program file extension @@ -190,109 +190,103 @@ fn upgrade_bpf_program( ); } -fn run_program( - name: &str, - loader_id: &Pubkey, - program_id: &Pubkey, - parameter_accounts: Vec, - instruction_data: &[u8], -) -> Result { +fn run_program(name: &str) -> u64 { let mut file = File::open(create_bpf_path(name)).unwrap(); let mut data = vec![]; file.read_to_end(&mut data).unwrap(); + let loader_id = bpf_loader::id(); + with_mock_invoke_context(loader_id, 0, |invoke_context| { + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); + let (parameter_bytes, account_lengths) = serialize_parameters( + &keyed_accounts[0].unsigned_key(), + &keyed_accounts[1].unsigned_key(), + &keyed_accounts[2..], + &[], + ) + .unwrap(); - let mut invoke_context = MockInvokeContext::new(&program_id, parameter_accounts); - let (parameter_bytes, account_lengths) = serialize_parameters( - &loader_id, - program_id, - &invoke_context.get_keyed_accounts().unwrap()[1..], - &instruction_data, - ) - .unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; - - let config = Config { - enable_instruction_tracing: true, - ..Config::default() - }; - let mut executable = >::from_elf( - &data, - None, - config, - register_syscalls(&mut invoke_context).unwrap(), - ) - .unwrap(); - executable.jit_compile().unwrap(); + let compute_meter = invoke_context.get_compute_meter(); + let mut instruction_meter = ThisInstructionMeter { compute_meter }; + let config = Config { + enable_instruction_tracing: true, + ..Config::default() + }; + let mut executable = >::from_elf( + &data, + None, + config, + register_syscalls(invoke_context).unwrap(), + ) + .unwrap(); + executable.jit_compile().unwrap(); - let mut instruction_count = 0; - let mut tracer = None; - for i in 0..2 { - let mut parameter_bytes = parameter_bytes.clone(); - { + let mut instruction_count = 0; + let mut tracer = None; + for i in 0..2 { invoke_context.set_return_data(Vec::new()).unwrap(); - - let mut vm = create_vm( - &loader_id, - executable.as_ref(), - parameter_bytes.as_slice_mut(), - &mut invoke_context, - &account_lengths, - ) - .unwrap(); - let result = if i == 0 { - vm.execute_program_interpreted(&mut instruction_meter) - } else { - vm.execute_program_jit(&mut instruction_meter) - }; - assert_eq!(SUCCESS, result.unwrap()); - if i == 1 { - assert_eq!(instruction_count, vm.get_total_instruction_count()); - } - instruction_count = vm.get_total_instruction_count(); - if config.enable_instruction_tracing { + let mut parameter_bytes = parameter_bytes.clone(); + { + let mut vm = create_vm( + &loader_id, + executable.as_ref(), + parameter_bytes.as_slice_mut(), + invoke_context, + &account_lengths, + ) + .unwrap(); + let result = if i == 0 { + vm.execute_program_interpreted(&mut instruction_meter) + } else { + vm.execute_program_jit(&mut instruction_meter) + }; + assert_eq!(SUCCESS, result.unwrap()); if i == 1 { - if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) { - let analysis = Analysis::from_executable(executable.as_ref()); - let stdout = std::io::stdout(); - println!("TRACE (interpreted):"); - tracer - .as_ref() - .unwrap() - .write(&mut stdout.lock(), &analysis) - .unwrap(); - println!("TRACE (jit):"); - vm.get_tracer() - .write(&mut stdout.lock(), &analysis) - .unwrap(); - assert!(false); - } else if log_enabled!(Trace) { - let analysis = Analysis::from_executable(executable.as_ref()); - let mut trace_buffer = Vec::::new(); - tracer - .as_ref() - .unwrap() - .write(&mut trace_buffer, &analysis) - .unwrap(); - let trace_string = String::from_utf8(trace_buffer).unwrap(); - trace!("BPF Program Instruction Trace:\n{}", trace_string); + assert_eq!(instruction_count, vm.get_total_instruction_count()); + } + instruction_count = vm.get_total_instruction_count(); + if config.enable_instruction_tracing { + if i == 1 { + if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) { + let analysis = Analysis::from_executable(executable.as_ref()); + let stdout = std::io::stdout(); + println!("TRACE (interpreted):"); + tracer + .as_ref() + .unwrap() + .write(&mut stdout.lock(), &analysis) + .unwrap(); + println!("TRACE (jit):"); + vm.get_tracer() + .write(&mut stdout.lock(), &analysis) + .unwrap(); + assert!(false); + } else if log_enabled!(Trace) { + let analysis = Analysis::from_executable(executable.as_ref()); + let mut trace_buffer = Vec::::new(); + tracer + .as_ref() + .unwrap() + .write(&mut trace_buffer, &analysis) + .unwrap(); + let trace_string = String::from_utf8(trace_buffer).unwrap(); + trace!("BPF Program Instruction Trace:\n{}", trace_string); + } } + tracer = Some(vm.get_tracer().clone()); } - tracer = Some(vm.get_tracer().clone()); } + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); + deserialize_parameters( + &loader_id, + &keyed_accounts[2..], + parameter_bytes.as_slice(), + &account_lengths, + true, + ) + .unwrap(); } - let parameter_accounts = invoke_context.get_keyed_accounts().unwrap(); - deserialize_parameters( - &loader_id, - parameter_accounts, - parameter_bytes.as_slice(), - &account_lengths, - true, - ) - .unwrap(); - } - - Ok(instruction_count) + instruction_count + }) } fn process_transaction_and_record_inner( @@ -1409,17 +1403,7 @@ fn assert_instruction_count() { let mut passed = true; println!("\n {:36} expected actual diff", "BPF program"); for program in programs.iter() { - let loader_id = bpf_loader::id(); - let program_id = Pubkey::new_unique(); - let key = Pubkey::new_unique(); - let mut program_account = RefCell::new(AccountSharedData::new(0, 0, &loader_id)); - let mut account = RefCell::new(AccountSharedData::default()); - let parameter_accounts = vec![ - KeyedAccount::new(&program_id, false, &mut program_account), - KeyedAccount::new(&key, false, &mut account), - ]; - let count = - run_program(program.0, &loader_id, &program_id, parameter_accounts, &[]).unwrap(); + let count = run_program(program.0); let diff: i64 = count as i64 - program.1 as i64; println!( " {:36} {:8} {:6} {:+5} ({:+3.0}%)", diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 73ed43f95af62e..23b91f143ab9fd 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -1043,7 +1043,7 @@ mod tests { use super::*; use rand::Rng; use solana_rbpf::vm::SyscallRegistry; - use solana_runtime::{bank::Bank, bank_client::BankClient}; + use solana_runtime::{bank::Bank, bank_client::BankClient, message_processor}; use solana_sdk::{ account::{ create_account_shared_data_for_test as create_account_for_test, AccountSharedData, @@ -1053,13 +1053,9 @@ mod tests { clock::Clock, feature_set::FeatureSet, genesis_config::create_genesis_config, - instruction::Instruction, - instruction::{AccountMeta, InstructionError}, - keyed_account::create_keyed_accounts_unified, + instruction::{AccountMeta, Instruction, InstructionError}, message::Message, - native_loader, native_token::LAMPORTS_PER_SOL, - process_instruction::{MockComputeMeter, MockInvokeContext}, pubkey::Pubkey, rent::Rent, signature::{Keypair, Signer}, @@ -1080,19 +1076,18 @@ mod tests { } } - type KeyedAccountTuple<'a> = (bool, bool, &'a Pubkey, &'a RefCell); fn process_instruction( - owner: &Pubkey, + loader_id: &Pubkey, + program_indices: &[usize], instruction_data: &[u8], - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - let processor_account = AccountSharedData::new_ref(0, 0, &native_loader::id()); - let mut keyed_accounts = keyed_accounts.to_vec(); - keyed_accounts.insert(0, (false, false, owner, &processor_account)); - super::process_instruction( - 1, + message_processor::mock_process_instruction( + loader_id, + program_indices.to_vec(), instruction_data, - &mut MockInvokeContext::new(owner, create_keyed_accounts_unified(&keyed_accounts)), + keyed_accounts, + super::process_instruction, ) } @@ -1151,9 +1146,9 @@ mod tests { #[test] fn test_bpf_loader_write() { let loader_id = bpf_loader::id(); - let program_key = solana_sdk::pubkey::new_rand(); + let program_id = Pubkey::new_unique(); let program_account = AccountSharedData::new_ref(1, 0, &loader_id); - let mut keyed_accounts: Vec = vec![]; + let mut keyed_accounts = vec![]; let instruction_data = bincode::serialize(&LoaderInstruction::Write { offset: 3, bytes: vec![1, 2, 3], @@ -1163,22 +1158,22 @@ mod tests { // Case: No program account assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); // Case: Not signed - keyed_accounts.push((false, false, &program_key, &program_account)); + keyed_accounts.push((false, false, program_id, program_account)); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); // Case: Write bytes to an offset - keyed_accounts[0] = (true, false, &program_key, &program_account); + keyed_accounts[0].0 = true; keyed_accounts[0].3.borrow_mut().set_data(vec![0; 6]); assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); assert_eq!(&vec![0, 0, 0, 1, 2, 3], keyed_accounts[0].3.borrow().data()); @@ -1186,38 +1181,38 @@ mod tests { keyed_accounts[0].3.borrow_mut().set_data(vec![0; 5]); assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction(&bpf_loader::id(), &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); } #[test] fn test_bpf_loader_finalize() { let loader_id = bpf_loader::id(); - let program_key = solana_sdk::pubkey::new_rand(); + let program_id = Pubkey::new_unique(); let program_account = load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); program_account.borrow_mut().set_executable(false); // Case: No program account let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap(); - let mut keyed_accounts: Vec = vec![]; + let mut keyed_accounts = vec![]; assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); // Case: Not signed - keyed_accounts.push((false, false, &program_key, &program_account)); + keyed_accounts.push((false, false, program_id, program_account.clone())); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); // Case: Finalize - keyed_accounts[0] = (true, false, &program_key, &program_account); + keyed_accounts[0] = (true, false, program_id, program_account.clone()); assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); assert!(keyed_accounts[0].3.borrow().executable()); @@ -1227,125 +1222,136 @@ mod tests { program_account.borrow_mut().data_as_mut_slice()[0] = 0; // bad elf assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction(&loader_id, &instruction_data, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction_data, &keyed_accounts), ); } #[test] fn test_bpf_loader_invoke_main() { let loader_id = bpf_loader::id(); - let program_key = solana_sdk::pubkey::new_rand(); + let program_id = Pubkey::new_unique(); let program_account = load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); // Case: No program account - let mut keyed_accounts: Vec = vec![]; + let mut keyed_accounts = vec![]; assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction(&loader_id, &[], &keyed_accounts), + process_instruction(&loader_id, &[], &[], &keyed_accounts), ); // Case: Only a program account - keyed_accounts.push((false, false, &program_key, &program_account)); + keyed_accounts.push((false, false, program_id, program_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); // Case: Account not a program keyed_accounts[0].3.borrow_mut().set_executable(false); assert_eq!( - Err(InstructionError::InvalidInstructionData), - process_instruction(&loader_id, &[], &keyed_accounts), + Err(InstructionError::IncorrectProgramId), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); keyed_accounts[0].3.borrow_mut().set_executable(true); // Case: With program and parameter account let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - keyed_accounts.push((false, false, &program_key, ¶meter_account)); + keyed_accounts.push((false, false, program_id, parameter_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); // Case: With duplicate accounts - let duplicate_key = solana_sdk::pubkey::new_rand(); + let duplicate_key = Pubkey::new_unique(); let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); - keyed_accounts.push((false, false, &duplicate_key, ¶meter_account)); + keyed_accounts[1] = (false, false, duplicate_key, parameter_account.clone()); + keyed_accounts.push((false, false, duplicate_key, parameter_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); // Case: limited budget - let processor_account = AccountSharedData::new_ref(0, 0, &native_loader::id()); - let mut keyed_accounts = keyed_accounts.clone(); - keyed_accounts.insert(0, (false, false, &program_key, &processor_account)); - let mut invoke_context = - MockInvokeContext::new(&program_key, create_keyed_accounts_unified(&keyed_accounts)); - invoke_context.compute_meter = Rc::new(RefCell::new(MockComputeMeter::default())); assert_eq!( Err(InstructionError::ProgramFailedToComplete), - super::process_instruction(1, &[], &mut invoke_context) + message_processor::mock_process_instruction( + &loader_id, + vec![0], + &[], + &keyed_accounts, + |first_instruction_account: usize, + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext| { + let compute_meter = invoke_context.get_compute_meter(); + let remaining = compute_meter.borrow_mut().get_remaining(); + compute_meter.borrow_mut().consume(remaining).unwrap(); + super::process_instruction( + first_instruction_account, + instruction_data, + invoke_context, + ) + }, + ), ); } #[test] fn test_bpf_loader_serialize_unaligned() { let loader_id = bpf_loader_deprecated::id(); - let program_key = solana_sdk::pubkey::new_rand(); + let program_key = Pubkey::new_unique(); let program_account = load_program_account_from_elf(&loader_id, "test_elfs/noop_unaligned.so"); // Case: With program and parameter account let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - let mut keyed_accounts: Vec = vec![ - (false, false, &program_key, &program_account), - (false, false, &program_key, ¶meter_account), + let mut keyed_accounts = vec![ + (false, false, program_key, program_account), + (false, false, program_key, parameter_account), ]; assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); // Case: With duplicate accounts - let duplicate_key = solana_sdk::pubkey::new_rand(); + let duplicate_key = Pubkey::new_unique(); let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); - keyed_accounts.push((false, false, &duplicate_key, ¶meter_account)); + keyed_accounts[1] = (false, false, duplicate_key, parameter_account.clone()); + keyed_accounts.push((false, false, duplicate_key, parameter_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); } #[test] fn test_bpf_loader_serialize_aligned() { let loader_id = bpf_loader::id(); - let program_key = solana_sdk::pubkey::new_rand(); + let program_key = Pubkey::new_unique(); let program_account = load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); // Case: With program and parameter account let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - let mut keyed_accounts: Vec = vec![ - (false, false, &program_key, &program_account), - (false, false, &program_key, ¶meter_account), + let mut keyed_accounts = vec![ + (false, false, program_key, program_account), + (false, false, program_key, parameter_account), ]; assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); // Case: With duplicate accounts - let duplicate_key = solana_sdk::pubkey::new_rand(); + let duplicate_key = Pubkey::new_unique(); let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); - keyed_accounts.push(keyed_accounts[1]); + keyed_accounts[1] = (false, false, duplicate_key, parameter_account.clone()); + keyed_accounts.push((false, false, duplicate_key, parameter_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &[], &keyed_accounts), + process_instruction(&loader_id, &[0], &[], &keyed_accounts), ); } @@ -1368,13 +1374,13 @@ mod tests { ); // Case: Success - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (false, false, &authority_address, &authority_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (false, false, authority_address, authority_account), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1387,7 +1393,7 @@ mod tests { // Case: Already initialized assert_eq!( Err(InstructionError::AccountAlreadyInitialized), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1414,13 +1420,13 @@ mod tests { bytes: vec![42; 9], }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (true, false, &buffer_address, &buffer_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (true, false, buffer_address, buffer_account.clone()), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Write entire buffer @@ -1437,7 +1443,7 @@ mod tests { .unwrap(); assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1469,13 +1475,13 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (true, false, &buffer_address, &buffer_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (true, false, buffer_address, buffer_account.clone()), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1504,7 +1510,7 @@ mod tests { .unwrap(); assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: overflow offset @@ -1521,7 +1527,7 @@ mod tests { .unwrap(); assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Not signed @@ -1536,13 +1542,13 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (false, false, &buffer_address, &buffer_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (false, false, buffer_address, buffer_account.clone()), ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: wrong authority @@ -1558,13 +1564,13 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (true, false, &authority_address, &buffer_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (true, false, authority_address, buffer_account.clone()), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: None authority @@ -1581,7 +1587,7 @@ mod tests { .unwrap(); assert_eq!( Err(InstructionError::Immutable), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); } @@ -1738,16 +1744,23 @@ mod tests { // Invoke deployed program { - let programdata_account = RefCell::new(post_programdata_account); - let program_account = RefCell::new(post_program_account); - let program_address = program_keypair.pubkey(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), - (false, false, &program_address, &program_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + Rc::new(RefCell::new(post_programdata_account)), + ), + ( + false, + false, + program_keypair.pubkey(), + Rc::new(RefCell::new(post_program_account)), + ), ]; assert_eq!( Ok(()), - process_instruction(&program_address, &[], &keyed_accounts), + process_instruction(&bpf_loader_upgradeable::id(), &[0, 1], &[], &keyed_accounts), ); } @@ -2289,12 +2302,12 @@ mod tests { file.read_to_end(&mut elf_new).unwrap(); assert_ne!(elf_orig.len(), elf_new.len()); let rent = Rent::default(); - let rent_account = RefCell::new(create_account_for_test(&Rent::default())); + let rent_account = Rc::new(RefCell::new(create_account_for_test(&Rent::default()))); let slot = 42; - let clock_account = RefCell::new(create_account_for_test(&Clock { + let clock_account = Rc::new(RefCell::new(create_account_for_test(&Clock { slot, ..Clock::default() - })); + }))); let min_program_balance = 1.max(rent.minimum_balance(UpgradeableLoaderState::program_len().unwrap())); let min_programdata_balance = 1.max(rent.minimum_balance( @@ -2385,23 +2398,28 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + ( + false, + true, + programdata_address, + programdata_account.clone(), + ), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account.clone()), + (false, true, spill_address, spill_account.clone()), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); assert_eq!(0, buffer_account.borrow().lamports()); assert_eq!( @@ -2444,23 +2462,23 @@ mod tests { upgrade_authority_address: None, }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::Immutable), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: wrong authority @@ -2475,23 +2493,23 @@ mod tests { min_programdata_balance, ); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &invalid_upgrade_authority_address, - &upgrade_authority_account, + invalid_upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: authority did not sign @@ -2505,23 +2523,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( false, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Program account not executable @@ -2536,23 +2554,23 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().set_executable(false); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::AccountNotExecutable), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Program account now owned by loader @@ -2567,23 +2585,23 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().set_owner(Pubkey::new_unique()); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::IncorrectProgramId), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Program account not writable @@ -2597,23 +2615,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, false, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, false, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Program account not initialized @@ -2631,23 +2649,23 @@ mod tests { .borrow_mut() .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Program ProgramData account mismatch @@ -2662,28 +2680,28 @@ mod tests { min_programdata_balance, ); let invalid_programdata_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ + let keyed_accounts = vec![ ( false, true, - &invalid_programdata_address, - &programdata_account, + invalid_programdata_address, + programdata_account, ), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Buffer account not initialized @@ -2701,23 +2719,23 @@ mod tests { .borrow_mut() .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Buffer account too big @@ -2742,23 +2760,23 @@ mod tests { authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Test small buffer account @@ -2779,23 +2797,23 @@ mod tests { }) .unwrap(); truncate_data(&mut buffer_account.borrow_mut(), 5); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Mismatched buffer and program authority @@ -2809,23 +2827,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: None buffer authority @@ -2845,23 +2863,23 @@ mod tests { authority_address: None, }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account.clone()), + (false, false, clock_id, clock_account.clone()), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: None buffer and program authority @@ -2888,23 +2906,23 @@ mod tests { upgrade_authority_address: None, }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, true, &programdata_address, &programdata_account), - (false, true, &program_address, &program_account), - (false, true, &buffer_address, &buffer_account), - (false, true, &spill_address, &spill_account), - (false, false, &rent_id, &rent_account), - (false, false, &clock_id, &clock_account), + let keyed_accounts = vec![ + (false, true, programdata_address, programdata_account), + (false, true, program_address, program_account), + (false, true, buffer_address, buffer_account), + (false, true, spill_address, spill_account), + (false, false, rent_id, rent_account), + (false, false, clock_id, clock_account), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); } @@ -2936,24 +2954,29 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ( true, false, - &new_upgrade_authority_address, - &new_upgrade_authority_account, + new_upgrade_authority_address, + new_upgrade_authority_account.clone(), ), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); assert_eq!( @@ -2972,18 +2995,23 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), ( true, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); assert_eq!( @@ -3002,18 +3030,23 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), ( false, false, - &upgrade_authority_address, - &upgrade_authority_account, + upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: wrong authority @@ -3025,24 +3058,29 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), ( true, false, - &invalid_upgrade_authority_address, - &upgrade_authority_account, + invalid_upgrade_authority_address, + upgrade_authority_account.clone(), ), ( false, false, - &new_upgrade_authority_address, - &new_upgrade_authority_account, + new_upgrade_authority_address, + new_upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: No authority @@ -3054,22 +3092,23 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), ( true, false, - &invalid_upgrade_authority_address, - &upgrade_authority_account, + invalid_upgrade_authority_address, + upgrade_authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::Immutable), - process_instruction( - &loader_id, - &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &keyed_accounts, - ) + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Not a ProgramData account @@ -3080,22 +3119,18 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), + let keyed_accounts = vec![ + (false, false, programdata_address, programdata_account), ( true, false, - &invalid_upgrade_authority_address, - &upgrade_authority_account, + invalid_upgrade_authority_address, + upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction( - &loader_id, - &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &keyed_accounts, - ) + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); } @@ -3121,13 +3156,13 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let mut keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (true, false, &authority_address, &authority_account), + let mut keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (true, false, authority_address, authority_account.clone()), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -3144,10 +3179,10 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - keyed_accounts.push((false, false, &new_authority_address, &new_authority_account)); + keyed_accounts.push((false, false, new_authority_address, new_authority_account)); assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -3164,10 +3199,10 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - keyed_accounts[1] = (false, false, &authority_address, &authority_account); + keyed_accounts[1] = (false, false, authority_address, authority_account.clone()); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: wrong authority @@ -3178,10 +3213,15 @@ mod tests { }) .unwrap(); let invalid_authority_address = Pubkey::new_unique(); - keyed_accounts[1] = (true, false, &invalid_authority_address, &authority_account); + keyed_accounts[1] = ( + true, + false, + invalid_authority_address, + authority_account.clone(), + ); assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: No authority @@ -3193,11 +3233,7 @@ mod tests { .unwrap(); assert_eq!( Err(InstructionError::Immutable), - process_instruction( - &loader_id, - &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &keyed_accounts, - ) + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Not a Buffer account @@ -3210,11 +3246,7 @@ mod tests { keyed_accounts.pop(); assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction( - &loader_id, - &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &keyed_accounts, - ) + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: Set to no authority @@ -3224,10 +3256,10 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - keyed_accounts[1] = (true, false, &authority_address, &authority_account); + keyed_accounts[1] = (true, false, authority_address, authority_account); assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); } @@ -3253,14 +3285,14 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (false, false, &recipient_address, &recipient_account), - (true, false, &authority_address, &authority_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account.clone()), + (false, false, recipient_address, recipient_account.clone()), + (true, false, authority_address, authority_account.clone()), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); assert_eq!(0, buffer_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3275,19 +3307,19 @@ mod tests { }) .unwrap(); let incorrect_authority_address = Pubkey::new_unique(); - let keyed_accounts: Vec = vec![ - (false, false, &buffer_address, &buffer_account), - (false, false, &recipient_address, &recipient_account), + let keyed_accounts = vec![ + (false, false, buffer_address, buffer_account), + (false, false, recipient_address, recipient_account), ( true, false, - &incorrect_authority_address, - &authority_account, + incorrect_authority_address, + authority_account.clone(), ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); // Case: close an uninitialized account @@ -3302,13 +3334,18 @@ mod tests { .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); let recipient_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); - let keyed_accounts: Vec = vec![ - (false, false, &uninitialized_address, &uninitialized_account), - (false, false, &recipient_address, &recipient_account), + let keyed_accounts = vec![ + ( + false, + false, + uninitialized_address, + uninitialized_account.clone(), + ), + (false, false, recipient_address, recipient_account.clone()), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); assert_eq!(0, uninitialized_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3343,15 +3380,20 @@ mod tests { }) .unwrap(); let recipient_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), - (false, false, &recipient_address, &recipient_account), - (true, false, &authority_address, &authority_account), - (false, true, &program_address, &program_account), + let keyed_accounts = vec![ + ( + false, + false, + programdata_address, + programdata_account.clone(), + ), + (false, false, recipient_address, recipient_account.clone()), + (true, false, authority_address, authority_account), + (false, true, program_address, program_account.clone()), ]; assert_eq!( Ok(()), - process_instruction(&loader_id, &instruction, &keyed_accounts), + process_instruction(&loader_id, &[], &instruction, &keyed_accounts), ); assert_eq!(0, programdata_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3359,13 +3401,13 @@ mod tests { assert_eq!(state, UpgradeableLoaderState::Uninitialized); // Try to invoke closed account - let keyed_accounts: Vec = vec![ - (false, false, &programdata_address, &programdata_account), - (false, false, &program_address, &program_account), + let keyed_accounts = vec![ + (false, false, programdata_address, programdata_account), + (false, false, program_address, program_account), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction(&program_address, &[], &keyed_accounts), + process_instruction(&program_address, &[0, 1], &[], &keyed_accounts), ); } @@ -3395,8 +3437,8 @@ mod tests { #[test] #[ignore] fn test_fuzz() { - let loader_id = solana_sdk::pubkey::new_rand(); - let program_key = solana_sdk::pubkey::new_rand(); + let loader_id = bpf_loader::id(); + let program_id = Pubkey::new_unique(); // Create program account let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed"); @@ -3416,12 +3458,12 @@ mod tests { program_account.borrow_mut().set_executable(true); let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); - let keyed_accounts: Vec = vec![ - (false, false, &program_key, &program_account), - (false, false, &program_key, ¶meter_account), + let keyed_accounts = vec![ + (false, false, program_id, program_account), + (false, false, program_id, parameter_account), ]; - let _result = process_instruction(&bpf_loader::id(), &[], &keyed_accounts); + let _result = process_instruction(&loader_id, &[], &[], &keyed_accounts); }, ); } diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 195bf863de4070..b4c812820e5320 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -2430,13 +2430,14 @@ mod tests { use solana_rbpf::{ ebpf::HOST_ALIGN, memory_region::MemoryRegion, user_error::UserError, vm::Config, }; + use solana_runtime::{ + log_collector::LogCollector, + message_processor::{ThisComputeMeter, ThisInvokeContext, ThisLogger}, + }; use solana_sdk::{ - bpf_loader, - fee_calculator::FeeCalculator, - hash::hashv, - process_instruction::{MockComputeMeter, MockInvokeContext, MockLogger}, + bpf_loader, feature_set::FeatureSet, fee_calculator::FeeCalculator, hash::hashv, }; - use std::str::FromStr; + use std::{str::FromStr, sync::Arc}; macro_rules! assert_access_violation { ($result:expr, $va:expr, $len:expr) => { @@ -2762,10 +2763,7 @@ mod tests { ) .unwrap(); - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: string.len() as u64 - 1, - })); + let compute_meter = ThisComputeMeter::new_ref(string.len() as u64 - 1); let mut syscall_panic = SyscallPanic { compute_meter, loader_id: &bpf_loader::id(), @@ -2787,10 +2785,7 @@ mod tests { result ); - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: string.len() as u64, - })); + let compute_meter = ThisComputeMeter::new_ref(string.len() as u64); let mut syscall_panic = SyscallPanic { compute_meter, loader_id: &bpf_loader::id(), @@ -2812,17 +2807,6 @@ mod tests { fn test_syscall_sol_log() { let string = "Gaggablaghblagh!"; let addr = string.as_ptr() as *const _ as u64; - - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining: 1000000 })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_log = SyscallLog { - compute_meter, - logger, - loader_id: &bpf_loader::id(), - }; let config = Config::default(); let memory_mapping = MemoryMapping::new::( vec![ @@ -2838,21 +2822,39 @@ mod tests { &config, ) .unwrap(); + let log = Rc::new(LogCollector::default()); - let mut result: Result> = Ok(0); - syscall_sol_log.call( - 0x100000000, - string.len() as u64, - 0, - 0, - 0, - &memory_mapping, - &mut result, - ); - result.unwrap(); - assert_eq!(log.borrow().len(), 1); - assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!"); + { + let mut syscall_sol_log = SyscallLog { + compute_meter: ThisComputeMeter::new_ref(string.len() as u64), + logger: ThisLogger::new_ref(Some(log.clone())), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall_sol_log.call( + 0x100000000, + string.len() as u64, + 0, + 0, + 0, + &memory_mapping, + &mut result, + ); + result.unwrap(); + } + let log: Vec = match Rc::try_unwrap(log) { + Ok(log) => log.into(), + Err(_) => panic!("Unwrap failed"), + }; + assert_eq!(log.len(), 1); + assert_eq!(log[0], "Program log: Gaggablaghblagh!"); + + let mut syscall_sol_log = SyscallLog { + compute_meter: ThisComputeMeter::new_ref(string.len() as u64 * 3), + logger: ThisLogger::new_ref(None), + loader_id: &bpf_loader::id(), + }; let mut result: Result> = Ok(0); syscall_sol_log.call( 0x100000001, // AccessViolation @@ -2886,14 +2888,9 @@ mod tests { &mut result, ); - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: (string.len() as u64 * 2) - 1, - })); - let logger: Rc> = Rc::new(RefCell::new(MockLogger { log })); let mut syscall_sol_log = SyscallLog { - compute_meter, - logger, + compute_meter: ThisComputeMeter::new_ref((string.len() as u64 * 2) - 1), + logger: ThisLogger::new_ref(None), loader_id: &bpf_loader::id(), }; let mut result: Result> = Ok(0); @@ -2927,45 +2924,33 @@ mod tests { #[test] fn test_syscall_sol_log_u64() { - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: std::u64::MAX, - })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_log_u64 = SyscallLogU64 { - cost: 0, - compute_meter, - logger, - }; - let config = Config::default(); - let memory_mapping = MemoryMapping::new::(vec![], &config).unwrap(); + let log = Rc::new(LogCollector::default()); - let mut result: Result> = Ok(0); - syscall_sol_log_u64.call(1, 2, 3, 4, 5, &memory_mapping, &mut result); - result.unwrap(); + { + let mut syscall_sol_log_u64 = SyscallLogU64 { + cost: 0, + compute_meter: ThisComputeMeter::new_ref(std::u64::MAX), + logger: ThisLogger::new_ref(Some(log.clone())), + }; + let config = Config::default(); + let memory_mapping = MemoryMapping::new::(vec![], &config).unwrap(); + let mut result: Result> = Ok(0); + syscall_sol_log_u64.call(1, 2, 3, 4, 5, &memory_mapping, &mut result); + result.unwrap(); + } - assert_eq!(log.borrow().len(), 1); - assert_eq!(log.borrow()[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5"); + let log: Vec = match Rc::try_unwrap(log) { + Ok(log) => log.into(), + Err(_) => panic!("Unwrap failed"), + }; + assert_eq!(log.len(), 1); + assert_eq!(log[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5"); } #[test] fn test_syscall_sol_pubkey() { let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap(); let addr = &pubkey.as_ref()[0] as *const _ as u64; - - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining: 2 })); - let log = Rc::new(RefCell::new(vec![])); - let logger: Rc> = - Rc::new(RefCell::new(MockLogger { log: log.clone() })); - let mut syscall_sol_pubkey = SyscallLogPubkey { - cost: 1, - compute_meter, - logger, - loader_id: &bpf_loader::id(), - }; let config = Config::default(); let memory_mapping = MemoryMapping::new::( vec![ @@ -2981,15 +2966,36 @@ mod tests { &config, ) .unwrap(); + let log = Rc::new(LogCollector::default()); - let mut result: Result> = Ok(0); - syscall_sol_pubkey.call(0x100000000, 0, 0, 0, 0, &memory_mapping, &mut result); - result.unwrap(); - assert_eq!(log.borrow().len(), 1); + { + let mut syscall_sol_pubkey = SyscallLogPubkey { + cost: 1, + compute_meter: ThisComputeMeter::new_ref(1), + logger: ThisLogger::new_ref(Some(log.clone())), + loader_id: &bpf_loader::id(), + }; + let mut result: Result> = Ok(0); + syscall_sol_pubkey.call(0x100000000, 0, 0, 0, 0, &memory_mapping, &mut result); + result.unwrap(); + } + + let log: Vec = match Rc::try_unwrap(log) { + Ok(log) => log.into(), + Err(_) => panic!("Unwrap failed"), + }; + assert_eq!(log.len(), 1); assert_eq!( - log.borrow()[0], + log[0], "Program log: MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN" ); + + let mut syscall_sol_pubkey = SyscallLogPubkey { + cost: 1, + compute_meter: ThisComputeMeter::new_ref(1), + logger: ThisLogger::new_ref(None), + loader_id: &bpf_loader::id(), + }; let mut result: Result> = Ok(0); syscall_sol_pubkey.call( 0x100000001, // AccessViolation @@ -3191,10 +3197,7 @@ mod tests { &config, ) .unwrap(); - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { - remaining: (bytes1.len() + bytes2.len()) as u64, - })); + let compute_meter = ThisComputeMeter::new_ref((bytes1.len() + bytes2.len()) as u64); let mut syscall = SyscallSha256 { sha256_base_cost: 0, sha256_byte_cost: 2, @@ -3281,11 +3284,15 @@ mod tests { leader_schedule_epoch: 4, unix_timestamp: 5, }; - let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_clock).unwrap(); - let sysvars = &[(sysvar::clock::id(), data)]; - invoke_context.sysvars = sysvars; + let sysvars = [(sysvar::clock::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); let mut syscall = SyscallGetClockSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3325,11 +3332,15 @@ mod tests { first_normal_epoch: 3, first_normal_slot: 4, }; - let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); - let sysvars = &[(sysvar::epoch_schedule::id(), data)]; - invoke_context.sysvars = sysvars; + let sysvars = [(sysvar::epoch_schedule::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); let mut syscall = SyscallGetEpochScheduleSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3376,11 +3387,15 @@ mod tests { lamports_per_signature: 1, }, }; - let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_fees).unwrap(); - let sysvars = &[(sysvar::fees::id(), data)]; - invoke_context.sysvars = sysvars; + let sysvars = [(sysvar::fees::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); let mut syscall = SyscallGetFeesSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3418,11 +3433,15 @@ mod tests { exemption_threshold: 2.0, burn_percent: 3, }; - let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_rent).unwrap(); - let sysvars = &[(sysvar::rent::id(), data)]; - invoke_context.sysvars = sysvars; + let sysvars = [(sysvar::rent::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); let mut syscall = SyscallGetRentSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3530,11 +3549,9 @@ mod tests { program_id: &Pubkey, remaining: u64, ) -> Result> { - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining })); let mut syscall = SyscallCreateProgramAddress { cost: 1, - compute_meter: compute_meter.clone(), + compute_meter: ThisComputeMeter::new_ref(remaining), loader_id: &bpf_loader::id(), }; let (address, _) = call_program_address_common(seeds, program_id, &mut syscall)?; @@ -3546,11 +3563,9 @@ mod tests { program_id: &Pubkey, remaining: u64, ) -> Result<(Pubkey, u8), EbpfError> { - let compute_meter: Rc> = - Rc::new(RefCell::new(MockComputeMeter { remaining })); let mut syscall = SyscallTryFindProgramAddress { cost: 1, - compute_meter: compute_meter.clone(), + compute_meter: ThisComputeMeter::new_ref(remaining), loader_id: &bpf_loader::id(), }; call_program_address_common(seeds, program_id, &mut syscall) diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 83f2bc552c79ac..9a375ca349ae70 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -539,6 +539,45 @@ pub fn prepare_mock_invoke_context( } } +pub fn with_mock_invoke_context R>( + loader_id: Pubkey, + account_size: usize, + mut callback: F, +) -> R { + let program_indices = vec![0, 1]; + let keyed_accounts = [ + ( + false, + false, + loader_id, + AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()), + ), + ( + false, + false, + Pubkey::new_unique(), + AccountSharedData::new_ref(1, 0, &loader_id), + ), + ( + false, + false, + Pubkey::new_unique(), + AccountSharedData::new_ref(2, account_size, &Pubkey::new_unique()), + ), + ]; + let preparation = prepare_mock_invoke_context(&program_indices, &[], &keyed_accounts); + let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); + invoke_context + .push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + ) + .unwrap(); + callback(&mut invoke_context) +} + pub fn mock_process_instruction( loader_id: &Pubkey, mut program_indices: Vec, From 0f94593a1eacd48a3460853139cf91b1881101f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 25 Oct 2021 09:23:44 +0200 Subject: [PATCH 2/9] Replaces MockInvokeContext by ThisInvokeContext in SystemInstructionProcessor. --- runtime/src/nonce_keyed_account.rs | 16 +- runtime/src/system_instruction_processor.rs | 572 +++++++++----------- 2 files changed, 276 insertions(+), 312 deletions(-) diff --git a/runtime/src/nonce_keyed_account.rs b/runtime/src/nonce_keyed_account.rs index dda439f190a2e9..95301b8d8524f5 100644 --- a/runtime/src/nonce_keyed_account.rs +++ b/runtime/src/nonce_keyed_account.rs @@ -254,15 +254,14 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { #[cfg(test)] mod test { use super::*; + use crate::message_processor::ThisInvokeContext; use solana_sdk::{ account::ReadableAccount, account_utils::State as AccountUtilsState, hash::{hash, Hash}, keyed_account::KeyedAccount, nonce::{self, State}, - nonce_account::create_account, - nonce_account::verify_nonce_account, - process_instruction::MockInvokeContext, + nonce_account::{create_account, verify_nonce_account}, system_instruction::SystemError, }; @@ -283,11 +282,11 @@ mod test { ) } - fn create_invoke_context_with_blockhash<'a>(seed: usize) -> MockInvokeContext<'a> { - let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); + fn create_invoke_context_with_blockhash<'a>(seed: usize) -> ThisInvokeContext<'a> { + let mut invoke_context = ThisInvokeContext::new_mock(&[], &[]); let (blockhash, lamports_per_signature) = create_test_blockhash(seed); - invoke_context.blockhash = blockhash; - invoke_context.lamports_per_signature = lamports_per_signature; + invoke_context.set_blockhash(blockhash); + invoke_context.set_lamports_per_signature(lamports_per_signature); invoke_context } @@ -974,12 +973,11 @@ mod test { let min_lamports = rent.minimum_balance(State::size()); with_test_keyed_account(min_lamports + 42, true, |nonce_account| { let mut signers = HashSet::new(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); signers.insert(*nonce_account.signer_key().unwrap()); let result = nonce_account.authorize_nonce_account( &Pubkey::default(), &signers, - &invoke_context, + &ThisInvokeContext::new_mock(&[], &[]), ); assert_eq!(result, Err(InstructionError::InvalidAccountData)); }) diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index 2c9a278861dc77..a6835ab650261c 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -479,27 +479,29 @@ pub fn get_system_account_kind(account: &AccountSharedData) -> Option for Address { fn from(address: Pubkey) -> Self { @@ -511,50 +513,46 @@ mod tests { } fn process_instruction( - owner: &Pubkey, instruction_data: &[u8], - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - let processor_account = RefCell::new(AccountSharedData::from(Account { - owner: solana_sdk::native_loader::id(), - ..Account::default() - })); - let mut keyed_accounts = keyed_accounts.to_vec(); - keyed_accounts.insert(0, (false, false, owner, &processor_account)); - super::process_instruction( - 1, + mock_process_instruction( + &system_program::id(), + Vec::new(), instruction_data, - &mut MockInvokeContext::new(owner, create_keyed_accounts_unified(&keyed_accounts)), + keyed_accounts, + super::process_instruction, ) } - fn create_default_account() -> RefCell { - RefCell::new(AccountSharedData::default()) + fn create_default_account() -> Rc> { + AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()) } - fn create_default_recent_blockhashes_account() -> RefCell { - RefCell::new( + fn create_default_recent_blockhashes_account() -> Rc> { + Rc::new(RefCell::new( #[allow(deprecated)] recent_blockhashes_account::create_account_with_data_for_test( vec![IterItem(0u64, &Hash::default(), 0); sysvar::recent_blockhashes::MAX_ENTRIES] .into_iter(), ), - ) + )) } - fn create_default_rent_account() -> RefCell { - RefCell::new(account::create_account_shared_data_for_test(&Rent::free())) + fn create_default_rent_account() -> Rc> { + Rc::new(RefCell::new(account::create_account_shared_data_for_test( + &Rent::free(), + ))) } #[test] fn test_create_account() { let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); - let to = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); + let to = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let to_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); assert_eq!( process_instruction( - &Pubkey::default(), &bincode::serialize(&SystemInstruction::CreateAccount { lamports: 50, space: 2, @@ -562,8 +560,8 @@ mod tests { }) .unwrap(), &[ - (true, false, &from, &from_account), - (true, false, &to, &to_account), + (true, false, from, from_account.clone()), + (true, false, to, to_account.clone()), ], ), Ok(()) @@ -577,7 +575,7 @@ mod tests { #[test] fn test_create_account_with_seed() { let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let seed = "shiny pepper"; let to = Pubkey::create_with_seed(&from, seed, &new_owner).unwrap(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); @@ -585,7 +583,6 @@ mod tests { assert_eq!( process_instruction( - &Pubkey::default(), &bincode::serialize(&SystemInstruction::CreateAccountWithSeed { base: from, seed: seed.to_string(), @@ -595,8 +592,8 @@ mod tests { }) .unwrap(), &[ - (true, false, &from, &from_account), - (false, false, &to, &to_account), + (true, false, from, from_account.clone()), + (false, false, to, to_account.clone()), ], ), Ok(()) @@ -610,8 +607,8 @@ mod tests { #[test] fn test_create_account_with_seed_separate_base_account() { let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); - let base = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); + let base = Pubkey::new_unique(); let seed = "shiny pepper"; let to = Pubkey::create_with_seed(&base, seed, &new_owner).unwrap(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); @@ -620,7 +617,6 @@ mod tests { assert_eq!( process_instruction( - &Pubkey::default(), &bincode::serialize(&SystemInstruction::CreateAccountWithSeed { base, seed: seed.to_string(), @@ -630,9 +626,9 @@ mod tests { }) .unwrap(), &[ - (true, false, &from, &from_account), - (false, false, &to, &to_account), - (true, false, &base, &base_account), + (true, false, from, from_account.clone()), + (false, false, to, to_account.clone()), + (true, false, base, base_account), ], ), Ok(()) @@ -645,36 +641,30 @@ mod tests { #[test] fn test_address_create_with_seed_mismatch() { - let from = solana_sdk::pubkey::new_rand(); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); + let from = Pubkey::new_unique(); let seed = "dull boy"; - let to = solana_sdk::pubkey::new_rand(); - let owner = solana_sdk::pubkey::new_rand(); + let to = Pubkey::new_unique(); + let owner = Pubkey::new_unique(); assert_eq!( - Address::create( - &to, - Some((&from, seed, &owner)), - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ), + Address::create(&to, Some((&from, seed, &owner)), &invoke_context), Err(SystemError::AddressWithSeedMismatch.into()) ); } #[test] fn test_create_account_with_seed_missing_sig() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let seed = "dull boy"; let to = Pubkey::create_with_seed(&from, seed, &new_owner).unwrap(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let to_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); - let to_address = Address::create( - &to, - Some((&from, seed, &new_owner)), - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ) - .unwrap(); + let to_address = + Address::create(&to, Some((&from, seed, &new_owner)), &invoke_context).unwrap(); assert_eq!( create_account( @@ -685,7 +675,7 @@ mod tests { 2, &new_owner, &HashSet::new(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Err(InstructionError::MissingRequiredSignature) ); @@ -695,6 +685,7 @@ mod tests { #[test] fn test_create_with_zero_lamports() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // create account with zero lamports transferred let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -712,7 +703,7 @@ mod tests { 2, &new_owner, &[from, to].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Ok(()) ); @@ -728,12 +719,13 @@ mod tests { #[test] fn test_create_negative_lamports() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Attempt to create account with more lamports than remaining in from_account let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let to = solana_sdk::pubkey::new_rand(); + let to = Pubkey::new_unique(); let to_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); let result = create_account( @@ -744,17 +736,18 @@ mod tests { 2, &new_owner, &[from, to].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); } #[test] fn test_request_more_than_allowed_data_length() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let to_account = AccountSharedData::new_ref(0, 0, &system_program::id()); - let to = solana_sdk::pubkey::new_rand(); + let to = Pubkey::new_unique(); let signers = &[from, to].iter().cloned().collect::>(); let address = &to.into(); @@ -768,7 +761,7 @@ mod tests { MAX_PERMITTED_DATA_LENGTH + 1, &system_program::id(), signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert!(result.is_err()); assert_eq!( @@ -785,7 +778,7 @@ mod tests { MAX_PERMITTED_DATA_LENGTH, &system_program::id(), signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert!(result.is_ok()); assert_eq!(to_account.borrow().lamports(), 50); @@ -797,13 +790,14 @@ mod tests { #[test] fn test_create_already_in_use() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Attempt to create system account in account already owned by another program let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let original_program_owner = Pubkey::new(&[5; 32]); - let owned_key = solana_sdk::pubkey::new_rand(); + let owned_key = Pubkey::new_unique(); let owned_account = AccountSharedData::new_ref(0, 0, &original_program_owner); let unchanged_account = owned_account.clone(); @@ -818,7 +812,7 @@ mod tests { 2, &new_owner, signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); @@ -837,7 +831,7 @@ mod tests { 2, &new_owner, signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); let from_lamports = from_account.borrow().lamports(); @@ -855,7 +849,7 @@ mod tests { 2, &new_owner, signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); assert_eq!(from_lamports, 100); @@ -864,12 +858,13 @@ mod tests { #[test] fn test_create_unsigned() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Attempt to create an account without signing the transfer let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let owned_key = solana_sdk::pubkey::new_rand(); + let owned_key = Pubkey::new_unique(); let owned_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); let owned_address = owned_key.into(); @@ -883,7 +878,7 @@ mod tests { 2, &new_owner, &[owned_key].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); @@ -897,7 +892,7 @@ mod tests { 2, &new_owner, &[from].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); @@ -911,18 +906,19 @@ mod tests { 2, &new_owner, &[owned_key].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); } #[test] fn test_create_sysvar_invalid_id_with_feature() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Attempt to create system account in account already owned by another program - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let to = solana_sdk::pubkey::new_rand(); + let to = Pubkey::new_unique(); let to_account = AccountSharedData::new_ref(0, 0, &system_program::id()); let signers = [from, to].iter().cloned().collect::>(); @@ -937,18 +933,31 @@ mod tests { 2, &sysvar::id(), &signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Ok(())); } #[test] fn test_create_sysvar_invalid_id_without_feature() { + let mut feature_set = FeatureSet::all_enabled(); + feature_set + .active + .remove(&feature_set::rent_for_sysvars::id()); + feature_set + .inactive + .insert(feature_set::rent_for_sysvars::id()); + let invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &[], + Arc::new(feature_set), + ); // Attempt to create system account in account already owned by another program - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let to = solana_sdk::pubkey::new_rand(); + let to = Pubkey::new_unique(); let to_account = AccountSharedData::new_ref(0, 0, &system_program::id()); let signers = [from, to].iter().cloned().collect::>(); @@ -962,24 +971,20 @@ mod tests { 2, &sysvar::id(), &signers, - &MockInvokeContext { - disabled_features: vec![feature_set::rent_for_sysvars::id()] - .into_iter() - .collect(), - ..MockInvokeContext::new(&Pubkey::default(), vec![]) - }, + &invoke_context, ); assert_eq!(result, Err(SystemError::InvalidProgramId.into())); } #[test] fn test_create_data_populated() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Attempt to create system account in account with populated data let new_owner = Pubkey::new(&[9; 32]); - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - let populated_key = solana_sdk::pubkey::new_rand(); + let populated_key = Pubkey::new_unique(); let populated_account = AccountSharedData::from(Account { data: vec![0, 1, 2, 3], ..Account::default() @@ -1000,14 +1005,15 @@ mod tests { 2, &new_owner, &signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); } #[test] fn test_create_from_account_is_nonce_fail() { - let nonce = solana_sdk::pubkey::new_rand(); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); + let nonce = Pubkey::new_unique(); let nonce_account = AccountSharedData::new_ref_data( 42, &nonce::state::Versions::new_current(nonce::State::Initialized( @@ -1017,7 +1023,7 @@ mod tests { ) .unwrap(); let from = KeyedAccount::new(&nonce, true, &nonce_account); - let new = solana_sdk::pubkey::new_rand(); + let new = Pubkey::new_unique(); let new_account = AccountSharedData::new_ref(0, 0, &system_program::id()); @@ -1032,9 +1038,9 @@ mod tests { &new_address, 42, 0, - &solana_sdk::pubkey::new_rand(), + &Pubkey::new_unique(), &signers, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Err(InstructionError::InvalidArgument), ); @@ -1042,8 +1048,9 @@ mod tests { #[test] fn test_assign() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let new_owner = Pubkey::new(&[9; 32]); - let pubkey = solana_sdk::pubkey::new_rand(); + let pubkey = Pubkey::new_unique(); let mut account = AccountSharedData::new(100, 0, &system_program::id()); assert_eq!( @@ -1052,7 +1059,7 @@ mod tests { &pubkey.into(), &new_owner, &HashSet::new(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Err(InstructionError::MissingRequiredSignature) ); @@ -1064,17 +1071,16 @@ mod tests { &pubkey.into(), &system_program::id(), &HashSet::new(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Ok(()) ); - let account = RefCell::new(account); + let account = Rc::new(RefCell::new(account)); assert_eq!( process_instruction( - &Pubkey::default(), &bincode::serialize(&SystemInstruction::Assign { owner: new_owner }).unwrap(), - &[(true, false, &pubkey, &account)], + &[(true, false, pubkey, account)], ), Ok(()) ); @@ -1082,9 +1088,9 @@ mod tests { #[test] fn test_assign_to_sysvar_with_feature() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let new_owner = sysvar::id(); - - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let mut from_account = AccountSharedData::new(100, 0, &system_program::id()); assert_eq!( @@ -1093,7 +1099,7 @@ mod tests { &from.into(), &new_owner, &[from].iter().cloned().collect::>(), - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Ok(()) ); @@ -1101,9 +1107,21 @@ mod tests { #[test] fn test_assign_to_sysvar_without_feature() { + let mut feature_set = FeatureSet::all_enabled(); + feature_set + .active + .remove(&feature_set::rent_for_sysvars::id()); + feature_set + .inactive + .insert(feature_set::rent_for_sysvars::id()); + let invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &[], + &[], + &[], + Arc::new(feature_set), + ); let new_owner = sysvar::id(); - - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let mut from_account = AccountSharedData::new(100, 0, &system_program::id()); assert_eq!( @@ -1112,12 +1130,7 @@ mod tests { &from.into(), &new_owner, &[from].iter().cloned().collect::>(), - &MockInvokeContext { - disabled_features: vec![feature_set::rent_for_sysvars::id()] - .into_iter() - .collect(), - ..MockInvokeContext::new(&Pubkey::default(), vec![]) - }, + &invoke_context, ), Err(SystemError::InvalidProgramId.into()) ); @@ -1127,40 +1140,31 @@ mod tests { fn test_process_bogus_instruction() { // Attempt to assign with no accounts let instruction = SystemInstruction::Assign { - owner: solana_sdk::pubkey::new_rand(), + owner: Pubkey::new_unique(), }; let data = serialize(&instruction).unwrap(); - let result = process_instruction(&system_program::id(), &data, &[]); + let result = process_instruction(&data, &[]); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); // Attempt to transfer with no destination - let from = solana_sdk::pubkey::new_rand(); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let instruction = SystemInstruction::Transfer { lamports: 0 }; let data = serialize(&instruction).unwrap(); - let result = process_instruction( - &system_program::id(), - &data, - &[(true, false, &from, &from_account)], - ); + let result = process_instruction(&data, &[(true, false, from, from_account)]); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); } #[test] fn test_transfer_lamports() { - let from = solana_sdk::pubkey::new_rand(); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter let to = Pubkey::new(&[3; 32]); let to_account = AccountSharedData::new_ref(1, 0, &to); // account owner should not matter let from_keyed_account = KeyedAccount::new(&from, true, &from_account); let to_keyed_account = KeyedAccount::new(&to, false, &to_account); - transfer( - &from_keyed_account, - &to_keyed_account, - 50, - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ) - .unwrap(); + transfer(&from_keyed_account, &to_keyed_account, 50, &invoke_context).unwrap(); let from_lamports = from_keyed_account.account.borrow().lamports(); let to_lamports = to_keyed_account.account.borrow().lamports(); assert_eq!(from_lamports, 50); @@ -1168,24 +1172,13 @@ mod tests { // Attempt to move more lamports than remaining in from_account let from_keyed_account = KeyedAccount::new(&from, true, &from_account); - let result = transfer( - &from_keyed_account, - &to_keyed_account, - 100, - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ); + let result = transfer(&from_keyed_account, &to_keyed_account, 100, &invoke_context); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); assert_eq!(to_keyed_account.account.borrow().lamports(), 51); // test signed transfer of zero - assert!(transfer( - &from_keyed_account, - &to_keyed_account, - 0, - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ) - .is_ok()); + assert!(transfer(&from_keyed_account, &to_keyed_account, 0, &invoke_context).is_ok()); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); assert_eq!(to_keyed_account.account.borrow().lamports(), 51); @@ -1193,12 +1186,7 @@ mod tests { let from_keyed_account = KeyedAccount::new(&from, false, &from_account); assert_eq!( - transfer( - &from_keyed_account, - &to_keyed_account, - 0, - &MockInvokeContext::new(&Pubkey::default(), vec![]), - ), + transfer(&from_keyed_account, &to_keyed_account, 0, &invoke_context), Err(InstructionError::MissingRequiredSignature) ); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); @@ -1207,7 +1195,8 @@ mod tests { #[test] fn test_transfer_with_seed() { - let base = solana_sdk::pubkey::new_rand(); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); + let base = Pubkey::new_unique(); let base_account = AccountSharedData::new_ref(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter let from_base_keyed_account = KeyedAccount::new(&base, true, &base_account); let from_seed = "42"; @@ -1225,7 +1214,7 @@ mod tests { &from_owner, &to_keyed_account, 50, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ) .unwrap(); let from_lamports = from_keyed_account.account.borrow().lamports(); @@ -1242,7 +1231,7 @@ mod tests { &from_owner, &to_keyed_account, 100, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); @@ -1257,7 +1246,7 @@ mod tests { &from_owner, &to_keyed_account, 0, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ) .is_ok()); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); @@ -1266,7 +1255,8 @@ mod tests { #[test] fn test_transfer_lamports_from_nonce_account_fail() { - let from = solana_sdk::pubkey::new_rand(); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); + let from = Pubkey::new_unique(); let from_account = AccountSharedData::new_ref_data( 100, &nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data { @@ -1288,7 +1278,7 @@ mod tests { &KeyedAccount::new(&from, true, &from_account), &KeyedAccount::new(&to, false, &to_account), 50, - &MockInvokeContext::new(&Pubkey::default(), vec![]), + &invoke_context, ), Err(InstructionError::InvalidArgument), ) @@ -1303,7 +1293,7 @@ mod tests { let alice_keypair = Keypair::new(); let alice_pubkey = alice_keypair.pubkey(); let seed = "seed"; - let owner = solana_sdk::pubkey::new_rand(); + let owner = Pubkey::new_unique(); let alice_with_seed = Pubkey::create_with_seed(&alice_pubkey, seed, &owner).unwrap(); bank_client @@ -1344,8 +1334,8 @@ mod tests { let alice_pubkey = alice_keypair.pubkey(); let bob_pubkey = bob_keypair.pubkey(); - let program = solana_sdk::pubkey::new_rand(); - let collector = solana_sdk::pubkey::new_rand(); + let program = Pubkey::new_unique(); + let collector = Pubkey::new_unique(); let mint_lamports = 10000; let len1 = 123; @@ -1371,7 +1361,7 @@ mod tests { let bank = Arc::new(Bank::new_from_parent(&bank, &collector, bank.slot() + 1)); let bank_client = BankClient::new_shared(&bank); bank_client - .transfer_and_confirm(50, &alice_keypair, &solana_sdk::pubkey::new_rand()) + .transfer_and_confirm(50, &alice_keypair, &Pubkey::new_unique()) .unwrap(); // super fun time; callback chooses to .clean_accounts(None) or not @@ -1415,7 +1405,7 @@ mod tests { let alice_keypair = Keypair::new(); let alice_pubkey = alice_keypair.pubkey(); let seed = "seed"; - let owner = solana_sdk::pubkey::new_rand(); + let owner = Pubkey::new_unique(); let alice_with_seed = Pubkey::create_with_seed(&alice_pubkey, seed, &owner).unwrap(); bank_client @@ -1474,30 +1464,25 @@ mod tests { } fn process_nonce_instruction(instruction: &Instruction) -> Result<(), InstructionError> { - let accounts: Vec<_> = instruction + let accounts = instruction.accounts.iter().map(|meta| { + #[allow(deprecated)] + if sysvar::recent_blockhashes::check_id(&meta.pubkey) { + create_default_recent_blockhashes_account() + } else if sysvar::rent::check_id(&meta.pubkey) { + Rc::new(RefCell::new(account::create_account_shared_data_for_test( + &Rent::free(), + ))) + } else { + AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()) + } + }); + let keyed_accounts: Vec<_> = instruction .accounts .iter() - .map(|meta| { - #[allow(deprecated)] - RefCell::new(if sysvar::recent_blockhashes::check_id(&meta.pubkey) { - create_default_recent_blockhashes_account().into_inner() - } else if sysvar::rent::check_id(&meta.pubkey) { - account::create_account_shared_data_for_test(&Rent::free()) - } else { - AccountSharedData::default() - }) - }) + .zip(accounts) + .map(|(meta, account)| (meta.is_signer, meta.is_writable, meta.pubkey, account)) .collect(); - - { - let keyed_accounts: Vec<_> = instruction - .accounts - .iter() - .zip(accounts.iter()) - .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) - .collect(); - process_instruction(&Pubkey::default(), &instruction.data, &keyed_accounts) - } + process_instruction(&instruction.data, &keyed_accounts) } #[test] @@ -1515,7 +1500,6 @@ mod tests { fn test_process_nonce_ix_no_keyed_accs_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), &[], ), @@ -1527,9 +1511,8 @@ mod tests { fn test_process_nonce_ix_only_nonce_acc_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), - &[(true, false, &Pubkey::default(), &create_default_account())], + &[(true, false, Pubkey::default(), create_default_account())], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1539,16 +1522,15 @@ mod tests { fn test_process_nonce_ix_bad_recent_blockhash_state_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), &[ - (true, false, &Pubkey::default(), &create_default_account()), + (true, false, Pubkey::default(), create_default_account()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_account(), + sysvar::recent_blockhashes::id(), + create_default_account(), ), ], ), @@ -1558,58 +1540,57 @@ mod tests { #[test] fn test_process_nonce_ix_ok() { - let nonce_acc = nonce_account::create_account(1_000_000); + let nonce_account = Rc::new(nonce_account::create_account(1_000_000)); process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ - (true, false, &Pubkey::default(), &nonce_acc), + (true, false, Pubkey::default(), nonce_account.clone()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account(), + sysvar::rent::id(), + create_default_rent_account(), ), ], ) .unwrap(); - let blockhash = &hash(&serialize(&0).unwrap()); - let new_recent_blockhashes_account = RefCell::new( - #[allow(deprecated)] + let blockhash = hash(&serialize(&0).unwrap()); + #[allow(deprecated)] + let new_recent_blockhashes_account = Rc::new(RefCell::new( solana_sdk::recent_blockhashes_account::create_account_with_data_for_test( - vec![IterItem(0u64, blockhash, 0); sysvar::recent_blockhashes::MAX_ENTRIES] + vec![IterItem(0u64, &blockhash, 0); sysvar::recent_blockhashes::MAX_ENTRIES] .into_iter(), ), - ); - let owner = Pubkey::default(); + )); #[allow(deprecated)] let blockhash_id = sysvar::recent_blockhashes::id(); - let processor_account = RefCell::new(AccountSharedData::from(Account { - owner: solana_sdk::native_loader::id(), - ..Account::default() - })); let keyed_accounts = [ - (false, false, &Pubkey::default(), &processor_account), - (true, false, &owner, &nonce_acc), - (false, false, &blockhash_id, &new_recent_blockhashes_account), + (true, false, Pubkey::default(), nonce_account), + (false, false, blockhash_id, new_recent_blockhashes_account), ]; - let mut invoke_context = &mut MockInvokeContext::new( - &Pubkey::default(), - create_keyed_accounts_unified(&keyed_accounts), - ); - invoke_context.blockhash = *blockhash; assert_eq!( - super::process_instruction( - 1, + mock_process_instruction( + &system_program::id(), + Vec::new(), &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), - invoke_context, + &keyed_accounts, + |first_instruction_account: usize, + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext| { + invoke_context.set_blockhash(hash(&serialize(&0).unwrap())); + super::process_instruction( + first_instruction_account, + instruction_data, + invoke_context, + ) + }, ), Ok(()), ); @@ -1632,7 +1613,6 @@ mod tests { fn test_process_withdraw_ix_no_keyed_accs_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), &[], ), @@ -1644,9 +1624,8 @@ mod tests { fn test_process_withdraw_ix_only_nonce_acc_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), - &[(true, false, &Pubkey::default(), &create_default_account())], + &[(true, false, Pubkey::default(), create_default_account())], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1656,17 +1635,16 @@ mod tests { fn test_process_withdraw_ix_bad_recent_blockhash_state_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), &[ - (true, false, &Pubkey::default(), &create_default_account()), - (false, false, &Pubkey::default(), &create_default_account()), + (true, false, Pubkey::default(), create_default_account()), + (false, false, Pubkey::default(), create_default_account()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_account() + sysvar::recent_blockhashes::id(), + create_default_account() ), ], ), @@ -1678,24 +1656,23 @@ mod tests { fn test_process_withdraw_ix_bad_rent_state_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), &[ ( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), ), - (true, false, &Pubkey::default(), &create_default_account()), + (true, false, Pubkey::default(), create_default_account()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), - (false, false, &sysvar::rent::id(), &create_default_account()), + (false, false, sysvar::rent::id(), create_default_account()), ], ), Err(InstructionError::InvalidArgument), @@ -1706,28 +1683,27 @@ mod tests { fn test_process_withdraw_ix_ok() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), &[ ( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), ), - (true, false, &Pubkey::default(), &create_default_account()), + (true, false, Pubkey::default(), create_default_account()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account() + sysvar::rent::id(), + create_default_rent_account() ), ], ), @@ -1739,7 +1715,6 @@ mod tests { fn test_process_initialize_ix_no_keyed_accs_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[], ), @@ -1751,13 +1726,12 @@ mod tests { fn test_process_initialize_ix_only_nonce_acc_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), )], ), Err(InstructionError::NotEnoughAccountKeys), @@ -1768,21 +1742,20 @@ mod tests { fn test_process_initialize_bad_recent_blockhash_state_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ ( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), ), ( true, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_account() + sysvar::recent_blockhashes::id(), + create_default_account() ), ], ), @@ -1794,23 +1767,22 @@ mod tests { fn test_process_initialize_ix_bad_rent_state_fail() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ ( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), ), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), - (false, false, &sysvar::rent::id(), &create_default_account()), + (false, false, sysvar::rent::id(), create_default_account()), ], ), Err(InstructionError::InvalidArgument), @@ -1821,27 +1793,26 @@ mod tests { fn test_process_initialize_ix_ok() { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ ( true, false, - &Pubkey::default(), - &nonce_account::create_account(1_000_000), + Pubkey::default(), + Rc::new(nonce_account::create_account(1_000_000)), ), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account() + sysvar::rent::id(), + create_default_rent_account() ), ], ), @@ -1851,33 +1822,31 @@ mod tests { #[test] fn test_process_authorize_ix_ok() { - let nonce_acc = nonce_account::create_account(1_000_000); + let nonce_account = Rc::new(nonce_account::create_account(1_000_000)); process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ - (true, false, &Pubkey::default(), &nonce_acc), + (true, false, Pubkey::default(), nonce_account.clone()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account(), + sysvar::rent::id(), + create_default_rent_account(), ), ], ) .unwrap(); assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::AuthorizeNonceAccount(Pubkey::default())).unwrap(), - &[(true, false, &Pubkey::default(), &nonce_acc)], + &[(true, false, Pubkey::default(), nonce_account)], ), Ok(()), ); @@ -1942,7 +1911,7 @@ mod tests { &nonce::state::Versions::new_current(nonce::State::Initialized( nonce::state::Data::default(), )), - &solana_sdk::pubkey::new_rand(), + &Pubkey::new_unique(), ) .unwrap(); assert_eq!(get_system_account_kind(&nonce_account), None); @@ -1950,31 +1919,30 @@ mod tests { #[test] fn test_nonce_initialize_with_empty_recent_blockhashes_fail() { - let nonce_acc = nonce_account::create_account(1_000_000); - let new_recent_blockhashes_account = RefCell::new( - #[allow(deprecated)] + let nonce_account = Rc::new(nonce_account::create_account(1_000_000)); + #[allow(deprecated)] + let new_recent_blockhashes_account = Rc::new(RefCell::new( solana_sdk::recent_blockhashes_account::create_account_with_data_for_test( vec![].into_iter(), ), - ); + )); assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ - (true, false, &Pubkey::default(), &nonce_acc), + (true, false, Pubkey::default(), nonce_account), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &new_recent_blockhashes_account, + sysvar::recent_blockhashes::id(), + new_recent_blockhashes_account, ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account() + sysvar::rent::id(), + create_default_rent_account() ), ], ), @@ -1984,57 +1952,55 @@ mod tests { #[test] fn test_nonce_advance_with_empty_recent_blockhashes_fail() { - let nonce_acc = nonce_account::create_account(1_000_000); + let nonce_account = Rc::new(nonce_account::create_account(1_000_000)); process_instruction( - &Pubkey::default(), &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), &[ - (true, false, &Pubkey::default(), &nonce_acc), + (true, false, Pubkey::default(), nonce_account.clone()), ( false, false, #[allow(deprecated)] - &sysvar::recent_blockhashes::id(), - &create_default_recent_blockhashes_account(), + sysvar::recent_blockhashes::id(), + create_default_recent_blockhashes_account(), ), ( false, false, - &sysvar::rent::id(), - &create_default_rent_account(), + sysvar::rent::id(), + create_default_rent_account(), ), ], ) .unwrap(); - let blockhash = &hash(&serialize(&0).unwrap()); - let new_recent_blockhashes_account = RefCell::new( - #[allow(deprecated)] + #[allow(deprecated)] + let new_recent_blockhashes_account = Rc::new(RefCell::new( solana_sdk::recent_blockhashes_account::create_account_with_data_for_test( vec![].into_iter(), ), - ); - let owner = Pubkey::default(); + )); #[allow(deprecated)] let blockhash_id = sysvar::recent_blockhashes::id(); - let processor_account = RefCell::new(AccountSharedData::from(Account { - owner: solana_sdk::native_loader::id(), - ..Account::default() - })); let keyed_accounts = [ - (false, false, &Pubkey::default(), &processor_account), - (true, false, &owner, &nonce_acc), - (false, false, &blockhash_id, &new_recent_blockhashes_account), + (true, false, Pubkey::default(), nonce_account), + (false, false, blockhash_id, new_recent_blockhashes_account), ]; - let mut invoke_context = &mut MockInvokeContext::new( - &Pubkey::default(), - create_keyed_accounts_unified(&keyed_accounts), - ); - invoke_context.blockhash = *blockhash; assert_eq!( - super::process_instruction( - 1, + mock_process_instruction( + &system_program::id(), + Vec::new(), &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), - invoke_context, + &keyed_accounts, + |first_instruction_account: usize, + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext| { + invoke_context.set_blockhash(hash(&serialize(&0).unwrap())); + super::process_instruction( + first_instruction_account, + instruction_data, + invoke_context, + ) + }, ), Err(NonceError::NoRecentBlockhashes.into()), ); From 7f78fc0df15698147d0d0dde79851fd29a0ee76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 27 Oct 2021 11:02:56 +0200 Subject: [PATCH 3/9] Replaces MockInvokeContext by ThisInvokeContext in CLIs. --- Cargo.lock | 2 + cli/Cargo.toml | 1 + cli/src/program.rs | 4 +- rbpf-cli/Cargo.toml | 1 + rbpf-cli/src/main.rs | 121 ++++++++++++++++++++++++++++--------------- 5 files changed, 84 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32d414ec9a4877..64281c4ad1ae5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3598,6 +3598,7 @@ dependencies = [ "serde_json", "solana-bpf-loader-program", "solana-logger 1.9.0", + "solana-runtime", "solana-sdk", "solana_rbpf", "time 0.3.4", @@ -4542,6 +4543,7 @@ dependencies = [ "solana-faucet", "solana-logger 1.9.0", "solana-remote-wallet", + "solana-runtime", "solana-sdk", "solana-streamer", "solana-test-validator", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 568f23e3e0fdb5..4fbde8a6d44d3f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -37,6 +37,7 @@ solana-faucet = { path = "../faucet", version = "=1.9.0" } solana-logger = { path = "../logger", version = "=1.9.0" } solana_rbpf = "=0.2.14" solana-remote-wallet = { path = "../remote-wallet", version = "=1.9.0" } +solana-runtime = { path = "../runtime", version = "=1.9.0" } solana-sdk = { path = "../sdk", version = "=1.9.0" } solana-transaction-status = { path = "../transaction-status", version = "=1.9.0" } solana-version = { path = "../version", version = "=1.9.0" } diff --git a/cli/src/program.rs b/cli/src/program.rs index 0f1022ee23c9cc..0f8cacb4ff9ee6 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -29,6 +29,7 @@ use solana_rbpf::{ vm::{Config, Executable}, }; use solana_remote_wallet::remote_wallet::RemoteWalletManager; +use solana_runtime::message_processor::ThisInvokeContext; use solana_sdk::{ account::Account, account_utils::StateMut, @@ -40,7 +41,6 @@ use solana_sdk::{ message::Message, native_token::Sol, packet::PACKET_DATA_SIZE, - process_instruction::MockInvokeContext, pubkey::Pubkey, signature::{keypair_from_seed, read_keypair_file, Keypair, Signature, Signer}, system_instruction::{self, SystemError}, @@ -1994,7 +1994,7 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box>::from_elf( diff --git a/rbpf-cli/Cargo.toml b/rbpf-cli/Cargo.toml index 6e8d156f4e30c7..767ca7a5a4af18 100644 --- a/rbpf-cli/Cargo.toml +++ b/rbpf-cli/Cargo.toml @@ -16,4 +16,5 @@ solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.9.0 solana-logger = { path = "../logger", version = "=1.9.0" } solana-sdk = { path = "../sdk", version = "=1.9.0" } solana_rbpf = "=0.2.14" +solana-runtime = { path = "../runtime", version = "=1.9.0" } time = "0.3.4" diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index df4bd47eeacb06..33ee445557b9b1 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -11,33 +11,33 @@ use solana_rbpf::{ verifier::check, vm::{Config, DynamicAnalysis, Executable}, }; +use solana_runtime::message_processor::{prepare_mock_invoke_context, ThisInvokeContext}; use solana_sdk::{ - account::AccountSharedData, - bpf_loader, - keyed_account::KeyedAccount, - process_instruction::{InvokeContext, MockInvokeContext}, - pubkey::Pubkey, + account::AccountSharedData, bpf_loader, process_instruction::InvokeContext, pubkey::Pubkey, }; -use std::{cell::RefCell, fs::File, io::Read, io::Seek, io::SeekFrom, path::Path}; +use std::{fs::File, io::Read, io::Seek, io::SeekFrom, path::Path}; use time::Instant; #[derive(Serialize, Deserialize, Debug)] struct Account { + key: Pubkey, + owner: Pubkey, + is_signer: bool, + is_writable: bool, lamports: u64, data: Vec, - owner: Pubkey, } #[derive(Serialize, Deserialize)] struct Input { accounts: Vec, - insndata: Vec, + instruction_data: Vec, } fn load_accounts(path: &Path) -> Result { let file = File::open(path).unwrap(); let input: Input = serde_json::from_reader(file)?; println!("Program input:"); println!("accounts {:?}", &input.accounts); - println!("insndata {:?}", &input.insndata); + println!("instruction_data {:?}", &input.instruction_data); println!("----------------------------------------"); Ok(input) } @@ -59,15 +59,21 @@ and the following fields are required { "accounts": [ { - "lamports": 1000, - "data": [0, 0, 0, 3], + "key": [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ], "owner": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] + ], + "is_signer": false, + "is_writable": true, + "lamports": 1000, + "data": [0, 0, 0, 3] } ], - "insndata": [] + "instruction_data": [] } "##, ) @@ -149,34 +155,69 @@ native machine code before execting it in the virtual machine.", enable_instruction_tracing: matches.is_present("trace") || matches.is_present("profile"), ..Config::default() }; - let mut accounts = Vec::new(); - let mut account_refcells = Vec::new(); - let default_account = RefCell::new(AccountSharedData::default()); - let key = solana_sdk::pubkey::new_rand(); - let (mut mem, account_lengths) = match matches.value_of("input").unwrap().parse::() { - Ok(allocate) => { - accounts.push(KeyedAccount::new(&key, false, &default_account)); - (vec![0u8; allocate], vec![allocate]) + let loader_id = bpf_loader::id(); + let mut keyed_accounts = vec![ + ( + false, + false, + loader_id, + AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()), + ), + ( + false, + false, + Pubkey::new_unique(), + AccountSharedData::new_ref(0, 0, &loader_id), + ), + ]; + let instruction_data = match matches.value_of("input").unwrap().parse::() { + Ok(allocation_size) => { + keyed_accounts.push(( + false, + true, + Pubkey::new_unique(), + AccountSharedData::new_ref(0, allocation_size, &Pubkey::new_unique()), + )); + vec![] } Err(_) => { let input = load_accounts(Path::new(matches.value_of("input").unwrap())).unwrap(); - for acc in input.accounts { - let asd = AccountSharedData::new_ref(acc.lamports, acc.data.len(), &acc.owner); - asd.borrow_mut().set_data(acc.data); - account_refcells.push(asd); + for account in input.accounts { + let account_refcell = AccountSharedData::new_ref( + account.lamports, + account.data.len(), + &account.owner, + ); + account_refcell.borrow_mut().set_data(account.data); + keyed_accounts.push(( + account.is_signer, + account.is_writable, + account.key, + account_refcell, + )); } - for acc in &account_refcells { - accounts.push(KeyedAccount::new(&key, false, acc)); - } - let lid = bpf_loader::id(); - let pid = Pubkey::new(&[0u8; 32]); - let (mut bytes, account_lengths) = - serialize_parameters(&lid, &pid, &accounts, &input.insndata).unwrap(); - (Vec::from(bytes.as_slice_mut()), account_lengths) + input.instruction_data } }; - let mut invoke_context = MockInvokeContext::new(&bpf_loader::id(), accounts); - let logger = invoke_context.logger.clone(); + let program_indices = [0, 1]; + let preparation = prepare_mock_invoke_context(&program_indices, &[], &keyed_accounts); + let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); + invoke_context + .push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + ) + .unwrap(); + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); + let (mut parameter_bytes, account_lengths) = serialize_parameters( + keyed_accounts[0].unsigned_key(), + keyed_accounts[1].unsigned_key(), + &keyed_accounts[2..], + &instruction_data, + ) + .unwrap(); let compute_meter = invoke_context.get_compute_meter(); let mut instruction_meter = ThisInstructionMeter { compute_meter }; @@ -231,11 +272,12 @@ native machine code before execting it in the virtual machine.", let mut vm = create_vm( &id, executable.as_ref(), - &mut mem, + parameter_bytes.as_slice_mut(), &mut invoke_context, &account_lengths, ) .unwrap(); + println!("Program output:"); let start_time = Instant::now(); let result = if matches.value_of("use").unwrap() == "interpreter" { vm.execute_program_interpreted(&mut instruction_meter) @@ -243,13 +285,6 @@ native machine code before execting it in the virtual machine.", vm.execute_program_jit(&mut instruction_meter) }; let duration = Instant::now() - start_time; - if logger.log.borrow().len() > 0 { - println!("Program output:"); - for s in logger.log.borrow_mut().iter() { - println!("{}", s); - } - println!("----------------------------------------"); - } println!("Result: {:?}", result); println!("Instruction Count: {}", vm.get_total_instruction_count()); println!("Execution time: {} us", duration.whole_microseconds()); From 672e3c788403d317afd50d75f6ab02a17c3a95ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 19 Oct 2021 10:31:37 +0200 Subject: [PATCH 4/9] Replaces MockInvokeContext by ThisInvokeContext in ConfigProcessor. --- Cargo.lock | 1 + programs/config/Cargo.toml | 1 + programs/config/src/config_processor.rs | 192 ++++++++++++------------ 3 files changed, 94 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64281c4ad1ae5c..b545dd98c090b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4664,6 +4664,7 @@ dependencies = [ "serde", "serde_derive", "solana-logger 1.9.0", + "solana-runtime", "solana-sdk", ] diff --git a/programs/config/Cargo.toml b/programs/config/Cargo.toml index 87302cead468bb..93c8f56b0a1f7f 100644 --- a/programs/config/Cargo.toml +++ b/programs/config/Cargo.toml @@ -18,6 +18,7 @@ solana-sdk = { path = "../../sdk", version = "=1.9.0" } [dev-dependencies] solana-logger = { path = "../../logger", version = "=1.9.0" } +solana-runtime = { path = "../../runtime", version = "=1.9.0" } [lib] crate-type = ["lib"] diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index 545dc5f0fcea72..bebd61a9ddaa7a 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -138,28 +138,25 @@ mod tests { use crate::{config_instruction, get_config_data, id, ConfigKeys, ConfigState}; use bincode::serialized_size; use serde_derive::{Deserialize, Serialize}; + use solana_runtime::message_processor; use solana_sdk::{ - account::{Account, AccountSharedData}, - keyed_account::create_keyed_accounts_unified, - process_instruction::MockInvokeContext, + account::AccountSharedData, pubkey::Pubkey, signature::{Keypair, Signer}, system_instruction::SystemInstruction, }; - use std::cell::RefCell; + use std::{cell::RefCell, rc::Rc}; fn process_instruction( - owner: &Pubkey, instruction_data: &[u8], - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); - let mut keyed_accounts = keyed_accounts.to_vec(); - keyed_accounts.insert(0, (false, false, owner, &processor_account)); - super::process_instruction( - 1, + message_processor::mock_process_instruction( + &id(), + Vec::new(), instruction_data, - &mut MockInvokeContext::new(owner, create_keyed_accounts_unified(&keyed_accounts)), + keyed_accounts, + super::process_instruction, ) } @@ -187,8 +184,10 @@ mod tests { } } - fn create_config_account(keys: Vec<(Pubkey, bool)>) -> (Keypair, RefCell) { - let from_pubkey = solana_sdk::pubkey::new_rand(); + fn create_config_account( + keys: Vec<(Pubkey, bool)>, + ) -> (Keypair, Rc>) { + let from_pubkey = Pubkey::new_unique(); let config_keypair = Keypair::new(); let config_pubkey = config_keypair.pubkey(); @@ -204,14 +203,10 @@ mod tests { } => space, _ => panic!("Not a CreateAccount system instruction"), }; - let config_account = RefCell::new(AccountSharedData::from(Account { - data: vec![0; space as usize], - owner: id(), - ..Account::default() - })); - let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; + let config_account = AccountSharedData::new_ref(0, space as usize, &id()); + let keyed_accounts = [(true, false, config_pubkey, config_account.clone())]; assert_eq!( - process_instruction(&id(), &instructions[1].data, &keyed_accounts), + process_instruction(&instructions[1].data, &keyed_accounts), Ok(()) ); @@ -237,9 +232,9 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = [(true, false, config_pubkey, config_account.clone())]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); assert_eq!( @@ -258,9 +253,9 @@ mod tests { let mut instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); instruction.data = vec![0; 123]; // <-- Replace data with a vector that's too large - let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = [(true, false, config_pubkey, config_account)]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::InvalidInstructionData) ); } @@ -275,9 +270,9 @@ mod tests { let mut instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config); instruction.accounts[0].is_signer = false; // <----- not a signer - let keyed_accounts = [(false, false, &config_pubkey, &config_account)]; + let keyed_accounts = [(false, false, config_pubkey, config_account)]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -285,9 +280,9 @@ mod tests { #[test] fn test_process_store_with_additional_signers() { solana_logger::setup(); - let pubkey = solana_sdk::pubkey::new_rand(); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); - let signer1_pubkey = solana_sdk::pubkey::new_rand(); + let pubkey = Pubkey::new_unique(); + let signer0_pubkey = Pubkey::new_unique(); + let signer1_pubkey = Pubkey::new_unique(); let keys = vec![ (pubkey, false), (signer0_pubkey, true), @@ -298,15 +293,15 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let signer0_account = RefCell::new(AccountSharedData::default()); - let signer1_account = RefCell::new(AccountSharedData::default()); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let signer1_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer1_pubkey, &signer1_account), + (true, false, config_pubkey, config_account.clone()), + (true, false, signer0_pubkey, signer0_account), + (true, false, signer1_pubkey, signer1_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -320,21 +315,18 @@ mod tests { #[test] fn test_process_store_without_config_signer() { solana_logger::setup(); - let pubkey = solana_sdk::pubkey::new_rand(); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); + let pubkey = Pubkey::new_unique(); + let signer0_pubkey = Pubkey::new_unique(); let keys = vec![(pubkey, false), (signer0_pubkey, true)]; let (config_keypair, _) = create_config_account(keys.clone()); let config_pubkey = config_keypair.pubkey(); let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); - let signer0_account = RefCell::new(AccountSharedData::from(Account { - owner: id(), - ..Account::default() - })); - let keyed_accounts = [(true, false, &signer0_pubkey, &signer0_account)]; + let signer0_account = AccountSharedData::new_ref(0, 0, &id()); + let keyed_accounts = [(true, false, signer0_pubkey, signer0_account)]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::InvalidAccountData) ); } @@ -342,10 +334,10 @@ mod tests { #[test] fn test_process_store_with_bad_additional_signer() { solana_logger::setup(); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); - let signer1_pubkey = solana_sdk::pubkey::new_rand(); - let signer0_account = RefCell::new(AccountSharedData::default()); - let signer1_account = RefCell::new(AccountSharedData::default()); + let signer0_pubkey = Pubkey::new_unique(); + let signer1_pubkey = Pubkey::new_unique(); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let signer1_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keys = vec![(signer0_pubkey, true)]; let (config_keypair, config_account) = create_config_account(keys.clone()); let config_pubkey = config_keypair.pubkey(); @@ -355,18 +347,18 @@ mod tests { // Config-data pubkey doesn't match signer let mut keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer1_pubkey, &signer1_account), + (true, false, config_pubkey, config_account), + (true, false, signer1_pubkey, signer1_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); // Config-data pubkey not a signer - keyed_accounts[1] = (false, false, &signer0_pubkey, &signer0_account); + keyed_accounts[1] = (false, false, signer0_pubkey, signer0_account); assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -374,13 +366,13 @@ mod tests { #[test] fn test_config_updates() { solana_logger::setup(); - let pubkey = solana_sdk::pubkey::new_rand(); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); - let signer1_pubkey = solana_sdk::pubkey::new_rand(); - let signer2_pubkey = solana_sdk::pubkey::new_rand(); - let signer0_account = RefCell::new(AccountSharedData::default()); - let signer1_account = RefCell::new(AccountSharedData::default()); - let signer2_account = RefCell::new(AccountSharedData::default()); + let pubkey = Pubkey::new_unique(); + let signer0_pubkey = Pubkey::new_unique(); + let signer1_pubkey = Pubkey::new_unique(); + let signer2_pubkey = Pubkey::new_unique(); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let signer1_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let signer2_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keys = vec![ (pubkey, false), (signer0_pubkey, true), @@ -392,12 +384,12 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); let mut keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer1_pubkey, &signer1_account), + (true, false, config_pubkey, config_account.clone()), + (true, false, signer0_pubkey, signer0_account), + (true, false, signer1_pubkey, signer1_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); @@ -407,7 +399,7 @@ mod tests { config_instruction::store(&config_pubkey, false, keys.clone(), &new_config); keyed_accounts[0].0 = false; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -423,7 +415,7 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); keyed_accounts[2].0 = false; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); @@ -434,9 +426,9 @@ mod tests { (signer2_pubkey, true), ]; let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); - keyed_accounts[2] = (true, false, &signer2_pubkey, &signer2_account); + keyed_accounts[2] = (true, false, signer2_pubkey, signer2_account); assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -446,7 +438,7 @@ mod tests { solana_logger::setup(); let config_address = Pubkey::new_unique(); let signer0_pubkey = Pubkey::new_unique(); - let signer0_account = RefCell::new(AccountSharedData::default()); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keys = vec![ (config_address, false), (signer0_pubkey, true), @@ -459,12 +451,12 @@ mod tests { // Attempt initialization with duplicate signer inputs let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); let keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer0_pubkey, &signer0_account), + (true, false, config_pubkey, config_account), + (true, false, signer0_pubkey, signer0_account.clone()), + (true, false, signer0_pubkey, signer0_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::InvalidArgument), ); } @@ -475,8 +467,8 @@ mod tests { let config_address = Pubkey::new_unique(); let signer0_pubkey = Pubkey::new_unique(); let signer1_pubkey = Pubkey::new_unique(); - let signer0_account = RefCell::new(AccountSharedData::default()); - let signer1_account = RefCell::new(AccountSharedData::default()); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let signer1_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keys = vec![ (config_address, false), (signer0_pubkey, true), @@ -488,12 +480,12 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); let mut keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer1_pubkey, &signer1_account), + (true, false, config_pubkey, config_account), + (true, false, signer0_pubkey, signer0_account), + (true, false, signer1_pubkey, signer1_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()), ); @@ -505,9 +497,9 @@ mod tests { (signer0_pubkey, true), ]; let instruction = config_instruction::store(&config_pubkey, false, dupe_keys, &new_config); - keyed_accounts[2] = keyed_accounts[1]; + keyed_accounts[2] = keyed_accounts[1].clone(); assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::InvalidArgument), ); } @@ -515,9 +507,9 @@ mod tests { #[test] fn test_config_updates_requiring_config() { solana_logger::setup(); - let pubkey = solana_sdk::pubkey::new_rand(); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); - let signer0_account = RefCell::new(AccountSharedData::default()); + let pubkey = Pubkey::new_unique(); + let signer0_pubkey = Pubkey::new_unique(); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let keys = vec![ (pubkey, false), (signer0_pubkey, true), @@ -535,11 +527,11 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); let keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), + (true, false, config_pubkey, config_account.clone()), + (true, false, signer0_pubkey, signer0_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); @@ -548,7 +540,7 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &new_config); assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -563,32 +555,32 @@ mod tests { let keys = vec![(pubkey, false), (config_keypair.pubkey(), true)]; let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts[0..1]), + process_instruction(&instruction.data, &keyed_accounts[0..1]), Err(InstructionError::MissingRequiredSignature) ); } #[test] fn test_config_initialize_no_panic() { - let from_pubkey = solana_sdk::pubkey::new_rand(); - let config_pubkey = solana_sdk::pubkey::new_rand(); + let from_pubkey = Pubkey::new_unique(); + let config_pubkey = Pubkey::new_unique(); let (_, _config_account) = create_config_account(vec![]); let instructions = config_instruction::create_account::(&from_pubkey, &config_pubkey, 1, vec![]); assert_eq!( - process_instruction(&id(), &instructions[1].data, &[]), + process_instruction(&instructions[1].data, &[]), Err(InstructionError::NotEnoughAccountKeys) ); } #[test] fn test_config_bad_owner() { - let from_pubkey = solana_sdk::pubkey::new_rand(); - let config_pubkey = solana_sdk::pubkey::new_rand(); + let from_pubkey = Pubkey::new_unique(); + let config_pubkey = Pubkey::new_unique(); let new_config = MyConfig::new(84); - let signer0_pubkey = solana_sdk::pubkey::new_rand(); - let signer0_account = RefCell::new(AccountSharedData::default()); - let config_account = RefCell::new(AccountSharedData::default()); + let signer0_pubkey = Pubkey::new_unique(); + let signer0_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); + let config_account = AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()); let (_, _config_account) = create_config_account(vec![]); let keys = vec![ (from_pubkey, false), @@ -598,11 +590,11 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys, &new_config); let keyed_accounts = [ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), + (true, false, config_pubkey, config_account), + (true, false, signer0_pubkey, signer0_account), ]; assert_eq!( - process_instruction(&id(), &instruction.data, &keyed_accounts), + process_instruction(&instruction.data, &keyed_accounts), Err(InstructionError::InvalidAccountOwner) ); } From 11a9280b621ccc36376926706e19c273be828a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 2 Nov 2021 14:23:47 +0100 Subject: [PATCH 5/9] Replaces MockInvokeContext by ThisInvokeContext in StakeProcessor. --- Cargo.lock | 1 + programs/stake/Cargo.toml | 1 + programs/stake/src/stake_instruction.rs | 467 +++++++++++------------- programs/stake/src/stake_state.rs | 27 +- 4 files changed, 233 insertions(+), 263 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b545dd98c090b7..f39299b503f35c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5733,6 +5733,7 @@ dependencies = [ "solana-frozen-abi-macro 1.9.0", "solana-logger 1.9.0", "solana-metrics", + "solana-runtime", "solana-sdk", "solana-vote-program", "thiserror", diff --git a/programs/stake/Cargo.toml b/programs/stake/Cargo.toml index c77e9381ef89f7..de7d71bf57d02f 100644 --- a/programs/stake/Cargo.toml +++ b/programs/stake/Cargo.toml @@ -27,6 +27,7 @@ thiserror = "1.0" [dev-dependencies] proptest = "1.0" solana-logger = { path = "../../logger", version = "=1.9.0" } +solana-runtime = { path = "../../runtime", version = "=1.9.0" } [build-dependencies] rustc_version = "0.4" diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 3041dfc4b3968c..63bc6da3685211 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -326,11 +326,13 @@ mod tests { use super::*; use crate::stake_state::{Meta, StakeState}; use bincode::serialize; + use solana_runtime::message_processor::{ + mock_process_instruction, prepare_mock_invoke_context, ThisInvokeContext, + }; use solana_sdk::{ - account::{self, Account, AccountSharedData, WritableAccount}, + account::{self, AccountSharedData}, + feature_set::FeatureSet, instruction::{AccountMeta, Instruction}, - keyed_account::create_keyed_accounts_unified, - process_instruction::MockInvokeContext, pubkey::Pubkey, rent::Rent, stake::{ @@ -340,17 +342,14 @@ mod tests { }, sysvar::{stake_history::StakeHistory, Sysvar}, }; - use std::{cell::RefCell, str::FromStr}; + use std::{cell::RefCell, rc::Rc, str::FromStr, sync::Arc}; - fn create_default_account() -> RefCell { - RefCell::new(AccountSharedData::default()) + fn create_default_account() -> Rc> { + AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()) } - fn create_default_stake_account() -> RefCell { - RefCell::new(AccountSharedData::from(Account { - owner: id(), - ..Account::default() - })) + fn create_default_stake_account() -> Rc> { + AccountSharedData::new_ref(0, 0, &id()) } fn invalid_stake_state_pubkey() -> Pubkey { @@ -370,91 +369,73 @@ mod tests { } fn process_instruction( - owner: &Pubkey, instruction_data: &[u8], - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); - let mut keyed_accounts = keyed_accounts.to_vec(); - keyed_accounts.insert(0, (false, false, owner, &processor_account)); - super::process_instruction( - 1, + mock_process_instruction( + &id(), + Vec::new(), instruction_data, - &mut MockInvokeContext::new(owner, create_keyed_accounts_unified(&keyed_accounts)), + keyed_accounts, + super::process_instruction, ) } fn process_instruction_as_one_arg(instruction: &Instruction) -> Result<(), InstructionError> { - let processor_account = RefCell::new(AccountSharedData::from(Account { - owner: solana_sdk::native_loader::id(), - ..Account::default() - })); - let accounts: Vec<_> = instruction + let accounts = instruction.accounts.iter().map(|meta| { + Rc::new(RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { + account::create_account_shared_data_for_test(&sysvar::clock::Clock::default()) + } else if sysvar::rewards::check_id(&meta.pubkey) { + account::create_account_shared_data_for_test(&sysvar::rewards::Rewards::new(0.0)) + } else if sysvar::stake_history::check_id(&meta.pubkey) { + account::create_account_shared_data_for_test(&StakeHistory::default()) + } else if stake_config::check_id(&meta.pubkey) { + config::create_account(0, &stake_config::Config::default()) + } else if sysvar::rent::check_id(&meta.pubkey) { + account::create_account_shared_data_for_test(&Rent::default()) + } else if meta.pubkey == invalid_stake_state_pubkey() { + AccountSharedData::new(0, 0, &id()) + } else if meta.pubkey == invalid_vote_state_pubkey() { + AccountSharedData::new(0, 0, &solana_vote_program::id()) + } else if meta.pubkey == spoofed_stake_state_pubkey() { + AccountSharedData::new(0, 0, &spoofed_stake_program_id()) + } else { + AccountSharedData::new(0, 0, &id()) + })) + }); + let keyed_accounts: Vec<_> = instruction .accounts .iter() - .map(|meta| { - RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { - account::create_account_shared_data_for_test(&sysvar::clock::Clock::default()) - } else if sysvar::rewards::check_id(&meta.pubkey) { - account::create_account_shared_data_for_test(&sysvar::rewards::Rewards::new( - 0.0, - )) - } else if sysvar::stake_history::check_id(&meta.pubkey) { - account::create_account_shared_data_for_test(&StakeHistory::default()) - } else if stake_config::check_id(&meta.pubkey) { - config::create_account(0, &stake_config::Config::default()) - } else if sysvar::rent::check_id(&meta.pubkey) { - account::create_account_shared_data_for_test(&Rent::default()) - } else if meta.pubkey == invalid_stake_state_pubkey() { - AccountSharedData::from(Account { - owner: id(), - ..Account::default() - }) - } else if meta.pubkey == invalid_vote_state_pubkey() { - AccountSharedData::from(Account { - owner: solana_vote_program::id(), - ..Account::default() - }) - } else if meta.pubkey == spoofed_stake_state_pubkey() { - AccountSharedData::from(Account { - owner: spoofed_stake_program_id(), - ..Account::default() - }) - } else { - AccountSharedData::from(Account { - owner: id(), - ..Account::default() - }) - }) - }) + .zip(accounts) + .map(|(meta, account)| (meta.is_signer, meta.is_writable, meta.pubkey, account)) .collect(); - - { - let mut keyed_accounts: Vec<_> = instruction - .accounts - .iter() - .zip(accounts.iter()) - .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) - .collect(); - let processor_id = id(); - keyed_accounts.insert(0, (false, false, &processor_id, &processor_account)); - let mut invoke_context = MockInvokeContext::new( - &processor_id, - create_keyed_accounts_unified(&keyed_accounts), - ); - let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of()); - bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap(); - let sysvars = &[(sysvar::clock::id(), data)]; - invoke_context.sysvars = sysvars; - super::process_instruction(1, &instruction.data, &mut invoke_context) - } + let mut preparation = prepare_mock_invoke_context(&[], &instruction.data, &keyed_accounts); + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + let program_indices = vec![preparation.accounts.len()]; + preparation.accounts.push((id(), processor_account)); + let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of()); + bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap(); + let sysvars = [(sysvar::clock::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &preparation.accounts, + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); + invoke_context.push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + )?; + super::process_instruction(1, &instruction.data, &mut invoke_context) } #[test] fn test_stake_process_instruction() { assert_eq!( process_instruction_as_one_arg(&instruction::initialize( - &Pubkey::default(), + &Pubkey::new_unique(), &Authorized::default(), &Lockup::default() )), @@ -462,9 +443,9 @@ mod tests { ); assert_eq!( process_instruction_as_one_arg(&instruction::authorize( - &Pubkey::default(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), StakeAuthorize::Staker, None, )), @@ -473,8 +454,8 @@ mod tests { assert_eq!( process_instruction_as_one_arg( &instruction::split( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 100, &invalid_stake_state_pubkey(), )[2] @@ -484,9 +465,9 @@ mod tests { assert_eq!( process_instruction_as_one_arg( &instruction::merge( - &Pubkey::default(), + &Pubkey::new_unique(), &invalid_stake_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), )[0] ), Err(InstructionError::InvalidAccountData), @@ -494,11 +475,11 @@ mod tests { assert_eq!( process_instruction_as_one_arg( &instruction::split_with_seed( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 100, &invalid_stake_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), "seed" )[1] ), @@ -506,17 +487,17 @@ mod tests { ); assert_eq!( process_instruction_as_one_arg(&instruction::delegate_stake( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), &invalid_vote_state_pubkey(), )), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction_as_one_arg(&instruction::withdraw( - &Pubkey::default(), - &Pubkey::default(), - &solana_sdk::pubkey::new_rand(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 100, None, )), @@ -524,16 +505,16 @@ mod tests { ); assert_eq!( process_instruction_as_one_arg(&instruction::deactivate_stake( - &Pubkey::default(), - &Pubkey::default() + &Pubkey::new_unique(), + &Pubkey::new_unique() )), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction_as_one_arg(&instruction::set_lockup( - &Pubkey::default(), + &Pubkey::new_unique(), &LockupArgs::default(), - &Pubkey::default() + &Pubkey::new_unique() )), Err(InstructionError::InvalidAccountData), ); @@ -552,8 +533,8 @@ mod tests { assert_eq!( process_instruction_as_one_arg(&instruction::authorize( &spoofed_stake_state_pubkey(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), StakeAuthorize::Staker, None, )), @@ -563,9 +544,9 @@ mod tests { process_instruction_as_one_arg( &instruction::split( &spoofed_stake_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), 100, - &Pubkey::default(), + &Pubkey::new_unique(), )[2] ), Err(InstructionError::InvalidAccountOwner), @@ -573,8 +554,8 @@ mod tests { assert_eq!( process_instruction_as_one_arg( &instruction::split( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 100, &spoofed_stake_state_pubkey(), )[2] @@ -585,8 +566,8 @@ mod tests { process_instruction_as_one_arg( &instruction::merge( &spoofed_stake_state_pubkey(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), )[0] ), Err(InstructionError::InvalidAccountOwner), @@ -594,9 +575,9 @@ mod tests { assert_eq!( process_instruction_as_one_arg( &instruction::merge( - &Pubkey::default(), + &Pubkey::new_unique(), &spoofed_stake_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), )[0] ), Err(InstructionError::IncorrectProgramId), @@ -605,10 +586,10 @@ mod tests { process_instruction_as_one_arg( &instruction::split_with_seed( &spoofed_stake_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), 100, - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), "seed" )[1] ), @@ -617,16 +598,16 @@ mod tests { assert_eq!( process_instruction_as_one_arg(&instruction::delegate_stake( &spoofed_stake_state_pubkey(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), )), Err(InstructionError::InvalidAccountOwner), ); assert_eq!( process_instruction_as_one_arg(&instruction::withdraw( &spoofed_stake_state_pubkey(), - &Pubkey::default(), - &solana_sdk::pubkey::new_rand(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 100, None, )), @@ -635,7 +616,7 @@ mod tests { assert_eq!( process_instruction_as_one_arg(&instruction::deactivate_stake( &spoofed_stake_state_pubkey(), - &Pubkey::default() + &Pubkey::new_unique() )), Err(InstructionError::InvalidAccountOwner), ); @@ -643,7 +624,7 @@ mod tests { process_instruction_as_one_arg(&instruction::set_lockup( &spoofed_stake_state_pubkey(), &LockupArgs::default(), - &Pubkey::default() + &Pubkey::new_unique() )), Err(InstructionError::InvalidAccountOwner), ); @@ -656,7 +637,6 @@ mod tests { // gets the "is_empty()" check assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() @@ -668,12 +648,11 @@ mod tests { ); // no account for rent - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let keyed_accounts = [(false, false, &stake_address, &stake_account)]; + let keyed_accounts = [(false, false, stake_address, stake_account)]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() @@ -685,17 +664,16 @@ mod tests { ); // rent fails to deserialize - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); let rent_address = sysvar::rent::id(); let rent_account = create_default_account(); let keyed_accounts = [ - (false, false, &stake_address, &stake_account), - (false, false, &rent_address, &rent_account), + (false, false, stake_address, stake_account), + (false, false, rent_address, rent_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() @@ -707,19 +685,18 @@ mod tests { ); // fails to deserialize stake state - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); let rent_address = sysvar::rent::id(); - let rent_account = RefCell::new(account::create_account_shared_data_for_test( + let rent_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &Rent::default(), - )); + ))); let keyed_accounts = [ - (false, false, &stake_address, &stake_account), - (false, false, &rent_address, &rent_account), + (false, false, stake_address, stake_account), + (false, false, rent_address, rent_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() @@ -731,12 +708,11 @@ mod tests { ); // gets the first check in delegate, wrong number of accounts - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let keyed_accounts = [(false, false, &stake_address, &stake_account)]; + let keyed_accounts = [(false, false, stake_address, stake_account)]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), &keyed_accounts, ), @@ -744,12 +720,11 @@ mod tests { ); // gets the sub-check for number of args - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let keyed_accounts = [(false, false, &stake_address, &stake_account)]; + let keyed_accounts = [(false, false, stake_address, stake_account)]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), &keyed_accounts, ), @@ -757,34 +732,33 @@ mod tests { ); // gets the check non-deserialize-able account in delegate_stake - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let vote_address = Pubkey::default(); - let mut bad_vote_account = create_default_account(); - bad_vote_account - .get_mut() - .set_owner(solana_vote_program::id()); + let vote_address = Pubkey::new_unique(); + let vote_account = AccountSharedData::new_ref(0, 0, &solana_vote_program::id()); let clock_address = sysvar::clock::id(); - let clock_account = RefCell::new(account::create_account_shared_data_for_test( + let clock_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &sysvar::clock::Clock::default(), - )); + ))); let stake_history_address = sysvar::stake_history::id(); - let stake_history_account = RefCell::new(account::create_account_shared_data_for_test( - &sysvar::stake_history::StakeHistory::default(), - )); + let stake_history_account = + Rc::new(RefCell::new(account::create_account_shared_data_for_test( + &sysvar::stake_history::StakeHistory::default(), + ))); let config_address = stake_config::id(); - let config_account = - RefCell::new(config::create_account(0, &stake_config::Config::default())); + let config_account = Rc::new(RefCell::new(config::create_account( + 0, + &stake_config::Config::default(), + ))); let keyed_accounts = [ - (true, false, &stake_address, &stake_account), - (false, false, &vote_address, &bad_vote_account), - (false, false, &clock_address, &clock_account), - (false, false, &stake_history_address, &stake_history_account), - (false, false, &config_address, &config_account), + (true, false, stake_address, stake_account), + (false, false, vote_address, vote_account), + (false, false, clock_address, clock_account), + (false, false, stake_history_address, stake_history_account), + (false, false, config_address, config_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), &keyed_accounts, ), @@ -792,27 +766,26 @@ mod tests { ); // Tests 3rd keyed account is of correct type (Clock instead of rewards) in withdraw - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let vote_address = Pubkey::default(); + let vote_address = Pubkey::new_unique(); let vote_account = create_default_account(); let rewards_address = sysvar::rewards::id(); - let rewards_account = RefCell::new(account::create_account_shared_data_for_test( + let rewards_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &sysvar::rewards::Rewards::new(0.0), - )); + ))); let stake_history_address = sysvar::stake_history::id(); - let stake_history_account = RefCell::new(account::create_account_shared_data_for_test( - &StakeHistory::default(), + let stake_history_account = Rc::new(RefCell::new( + account::create_account_shared_data_for_test(&StakeHistory::default()), )); let keyed_accounts = [ - (false, false, &stake_address, &stake_account), - (false, false, &vote_address, &vote_account), - (false, false, &rewards_address, &rewards_account), - (false, false, &stake_history_address, &stake_history_account), + (false, false, stake_address, stake_account), + (false, false, vote_address, vote_account), + (false, false, rewards_address, rewards_account), + (false, false, stake_history_address, stake_history_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Withdraw(42)).unwrap(), &keyed_accounts, ), @@ -820,12 +793,11 @@ mod tests { ); // Tests correct number of accounts are provided in withdraw - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); - let keyed_accounts = [(false, false, &stake_address, &stake_account)]; + let keyed_accounts = [(false, false, stake_address, stake_account)]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Withdraw(42)).unwrap(), &keyed_accounts, ), @@ -833,19 +805,18 @@ mod tests { ); // Tests 2nd keyed account is of correct type (Clock instead of rewards) in deactivate - let stake_address = Pubkey::default(); + let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); let rewards_address = sysvar::rewards::id(); - let rewards_account = RefCell::new(account::create_account_shared_data_for_test( + let rewards_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &sysvar::rewards::Rewards::new(0.0), - )); + ))); let keyed_accounts = [ - (false, false, &stake_address, &stake_account), - (false, false, &rewards_address, &rewards_account), + (false, false, stake_address, stake_account), + (false, false, rewards_address, rewards_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::Deactivate).unwrap(), &keyed_accounts, ), @@ -854,11 +825,7 @@ mod tests { // Tests correct number of accounts are provided in deactivate assert_eq!( - process_instruction( - &Pubkey::default(), - &serialize(&StakeInstruction::Deactivate).unwrap(), - &[], - ), + process_instruction(&serialize(&StakeInstruction::Deactivate).unwrap(), &[]), Err(InstructionError::NotEnoughAccountKeys), ); } @@ -885,21 +852,20 @@ mod tests { &id(), ); let rent_address = sysvar::rent::id(); - let rent_account = RefCell::new(account::create_account_shared_data_for_test( + let rent_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &Rent::default(), - )); + ))); let staker_account = create_default_account(); let withdrawer_account = create_default_account(); - let keyed_accounts: [(bool, bool, &Pubkey, &RefCell); 4] = [ - (false, false, &stake_address, &stake_account), - (false, false, &rent_address, &rent_account), - (false, false, &staker, &staker_account), - (true, false, &withdrawer, &withdrawer_account), + let keyed_accounts = [ + (false, false, stake_address, stake_account), + (false, false, rent_address, rent_account), + (false, false, staker, staker_account), + (true, false, withdrawer, withdrawer_account.clone()), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::InitializeChecked).unwrap(), &keyed_accounts, ), @@ -935,39 +901,39 @@ mod tests { ); // Test AuthorizeChecked with authority signer - let stake_account = AccountSharedData::new_ref_data_with_space( - 42, - &StakeState::Initialized(Meta::auto(&authorized_address)), - std::mem::size_of::(), - &id(), - ) - .unwrap(); + let stake_account = Rc::new( + AccountSharedData::new_ref_data_with_space( + 42, + &StakeState::Initialized(Meta::auto(&authorized_address)), + std::mem::size_of::(), + &id(), + ) + .unwrap(), + ); let clock_address = sysvar::clock::id(); - let clock_account = RefCell::new(account::create_account_shared_data_for_test( + let clock_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &Clock::default(), - )); + ))); let authorized_account = create_default_account(); let new_authorized_account = create_default_account(); let mut keyed_accounts = [ - (false, false, &stake_address, &stake_account), - (false, false, &clock_address, &clock_account), - (true, false, &authorized_address, &authorized_account), - (true, false, &staker, &new_authorized_account), + (false, false, stake_address, stake_account), + (false, false, clock_address, clock_account.clone()), + (true, false, authorized_address, authorized_account.clone()), + (true, false, staker, new_authorized_account.clone()), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeChecked(StakeAuthorize::Staker)).unwrap(), &keyed_accounts, ), Ok(()), ); - keyed_accounts[3] = (true, false, &withdrawer, &new_authorized_account); + keyed_accounts[3] = (true, false, withdrawer, new_authorized_account.clone()); assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeChecked( StakeAuthorize::Withdrawer )) @@ -1013,22 +979,23 @@ mod tests { ); // Test AuthorizeCheckedWithSeed with authority signer - let stake_account = AccountSharedData::new_ref_data_with_space( - 42, - &StakeState::Initialized(Meta::auto(&address_with_seed)), - std::mem::size_of::(), - &id(), - ) - .unwrap(); + let stake_account = Rc::new( + AccountSharedData::new_ref_data_with_space( + 42, + &StakeState::Initialized(Meta::auto(&address_with_seed)), + std::mem::size_of::(), + &id(), + ) + .unwrap(), + ); let mut keyed_accounts = [ - (false, false, &address_with_seed, &stake_account), - (true, false, &authorized_owner, &authorized_account), - (false, false, &clock_address, &clock_account), - (true, false, &staker, &new_authorized_account), + (false, false, address_with_seed, stake_account), + (true, false, authorized_owner, authorized_account), + (false, false, clock_address, clock_account), + (true, false, staker, new_authorized_account.clone()), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeCheckedWithSeed( AuthorizeCheckedWithSeedArgs { stake_authorize: StakeAuthorize::Staker, @@ -1042,10 +1009,9 @@ mod tests { Ok(()), ); - keyed_accounts[3] = (true, false, &withdrawer, &new_authorized_account); + keyed_accounts[3] = (true, false, withdrawer, new_authorized_account); assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeCheckedWithSeed( AuthorizeCheckedWithSeedArgs { stake_authorize: StakeAuthorize::Withdrawer, @@ -1077,42 +1043,45 @@ mod tests { ); // Test SetLockupChecked with lockup custodian signer - let stake_account = AccountSharedData::new_ref_data_with_space( - 42, - &StakeState::Initialized(Meta::auto(&withdrawer)), - std::mem::size_of::(), - &id(), - ) - .unwrap(); + let stake_account = Rc::new( + AccountSharedData::new_ref_data_with_space( + 42, + &StakeState::Initialized(Meta::auto(&withdrawer)), + std::mem::size_of::(), + &id(), + ) + .unwrap(), + ); let custodian_account = create_default_account(); - let processor_account = RefCell::new(AccountSharedData::from(Account { - owner: solana_sdk::native_loader::id(), - ..Account::default() - })); let keyed_accounts = [ - (false, false, &id(), &processor_account), - (false, false, &stake_address, &stake_account), - (true, false, &withdrawer, &withdrawer_account), - (true, false, &custodian, &custodian_account), + (false, false, stake_address, stake_account), + (true, false, withdrawer, withdrawer_account), + (true, false, custodian, custodian_account), ]; - let mut invoke_context = - MockInvokeContext::new(&id(), create_keyed_accounts_unified(&keyed_accounts)); + let mut preparation = prepare_mock_invoke_context(&[], &instruction.data, &keyed_accounts); + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + let program_indices = vec![preparation.accounts.len()]; + preparation.accounts.push((id(), processor_account)); let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of()); bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap(); - let sysvars = &[(sysvar::clock::id(), data)]; - invoke_context.sysvars = sysvars; - + let sysvars = [(sysvar::clock::id(), data)]; + let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( + &preparation.accounts, + &[], + &sysvars, + Arc::new(FeatureSet::all_enabled()), + ); + invoke_context + .push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + ) + .unwrap(); assert_eq!( - super::process_instruction( - 1, - &serialize(&StakeInstruction::SetLockupChecked(LockupCheckedArgs { - unix_timestamp: None, - epoch: Some(1), - })) - .unwrap(), - &mut invoke_context, - ), + super::process_instruction(1, &instruction.data, &mut invoke_context), Ok(()), ); } diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index c56ce321554759..4a95869ef49467 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -1373,11 +1373,11 @@ fn do_create_account( mod tests { use super::*; use proptest::prelude::*; + use solana_runtime::message_processor::ThisInvokeContext; use solana_sdk::{ account::{AccountSharedData, WritableAccount}, clock::UnixTimestamp, native_token, - process_instruction::MockInvokeContext, pubkey::Pubkey, system_program, }; @@ -5069,13 +5069,13 @@ mod tests { #[test] fn test_merge() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); let stake_lamports = 42; let signers = vec![authorized_pubkey].into_iter().collect(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); for state in &[ StakeState::Initialized(Meta::auto(&authorized_pubkey)), @@ -5179,7 +5179,7 @@ mod tests { #[test] fn test_merge_self_fails() { - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let stake_address = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique(); let signers = HashSet::from_iter(vec![authority_pubkey]); @@ -5224,6 +5224,7 @@ mod tests { #[test] fn test_merge_incorrect_authorized_staker() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5232,7 +5233,6 @@ mod tests { let signers = vec![authorized_pubkey].into_iter().collect(); let wrong_signers = vec![wrong_authorized_pubkey].into_iter().collect(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); for state in &[ StakeState::Initialized(Meta::auto(&authorized_pubkey)), @@ -5293,12 +5293,12 @@ mod tests { #[test] fn test_merge_invalid_account_data() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); let stake_lamports = 42; let signers = vec![authorized_pubkey].into_iter().collect(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); for state in &[ StakeState::Uninitialized, @@ -5343,6 +5343,7 @@ mod tests { #[test] fn test_merge_fake_stake_source() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5368,7 +5369,6 @@ mod tests { .expect("source_stake_account"); let source_stake_keyed_account = KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); assert_eq!( stake_keyed_account.merge( @@ -5385,6 +5385,7 @@ mod tests { #[test] fn test_merge_active_stake() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let base_lamports = 4242424242; let stake_address = Pubkey::new_unique(); let source_address = Pubkey::new_unique(); @@ -5438,7 +5439,6 @@ mod tests { let mut clock = Clock::default(); let mut stake_history = StakeHistory::default(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); clock.epoch = 0; let mut effective = base_lamports; @@ -6007,6 +6007,7 @@ mod tests { #[test] fn test_things_can_merge() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let good_stake = Stake { credits_observed: 4242, delegation: Delegation { @@ -6016,7 +6017,6 @@ mod tests { ..Delegation::default() }, }; - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); let identical = good_stake; assert!( @@ -6105,7 +6105,7 @@ mod tests { #[test] fn test_metas_can_merge_pre_v4() { - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6191,7 +6191,7 @@ mod tests { #[test] fn test_metas_can_merge_v4() { - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6337,6 +6337,7 @@ mod tests { #[test] fn test_merge_kind_get_if_mergeable() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let authority_pubkey = Pubkey::new_unique(); let initial_lamports = 4242424242; let rent = Rent::default(); @@ -6357,7 +6358,6 @@ mod tests { let stake_keyed_account = KeyedAccount::new(&authority_pubkey, true, &stake_account); let mut clock = Clock::default(); let mut stake_history = StakeHistory::default(); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); // Uninitialized state fails assert_eq!( @@ -6569,6 +6569,7 @@ mod tests { #[test] fn test_merge_kind_merge() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let lamports = 424242; let meta = Meta { rent_exempt_reserve: 42, @@ -6584,7 +6585,6 @@ mod tests { let inactive = MergeKind::Inactive(Meta::default(), lamports); let activation_epoch = MergeKind::ActivationEpoch(meta, stake); let fully_active = MergeKind::FullyActive(meta, stake); - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); assert_eq!( inactive @@ -6647,6 +6647,7 @@ mod tests { #[test] fn test_active_stake_merge() { + let invoke_context = ThisInvokeContext::new_mock(&[], &[]); let delegation_a = 4_242_424_242u64; let delegation_b = 6_200_000_000u64; let credits_a = 124_521_000u64; @@ -6670,8 +6671,6 @@ mod tests { credits_observed: credits_a, }; - let invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); - // activating stake merge, match credits observed let activation_epoch_a = MergeKind::ActivationEpoch(meta, stake_a); let activation_epoch_b = MergeKind::ActivationEpoch(meta, stake_b); From 18509db27e78a26a3b9d73d1c09b557fcf5294a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 19 Oct 2021 17:47:02 +0200 Subject: [PATCH 6/9] Replaces MockInvokeContext by ThisInvokeContext in VoteProcessor. --- Cargo.lock | 1 + programs/vote/Cargo.toml | 3 + programs/vote/src/vote_instruction.rs | 108 +++++++++++++------------- 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f39299b503f35c..4d58261b5ab7e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6004,6 +6004,7 @@ dependencies = [ "solana-frozen-abi-macro 1.9.0", "solana-logger 1.9.0", "solana-metrics", + "solana-runtime", "solana-sdk", "thiserror", ] diff --git a/programs/vote/Cargo.toml b/programs/vote/Cargo.toml index 16e611b2457936..6a8c76c6c05ac5 100644 --- a/programs/vote/Cargo.toml +++ b/programs/vote/Cargo.toml @@ -23,6 +23,9 @@ solana-metrics = { path = "../../metrics", version = "=1.9.0" } solana-sdk = { path = "../../sdk", version = "=1.9.0" } thiserror = "1.0" +[dev-dependencies] +solana-runtime = { path = "../../runtime", version = "=1.9.0" } + [build-dependencies] rustc_version = "0.4" diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index b1410fe4798c3c..e3078be7bc1d91 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -405,41 +405,37 @@ pub fn process_instruction( mod tests { use super::*; use bincode::serialize; + use solana_runtime::message_processor; use solana_sdk::{ account::{self, Account, AccountSharedData}, - keyed_account::create_keyed_accounts_unified, - process_instruction::MockInvokeContext, rent::Rent, }; - use std::cell::RefCell; use std::str::FromStr; + use std::{cell::RefCell, rc::Rc}; - fn create_default_account() -> RefCell { - RefCell::new(AccountSharedData::default()) + fn create_default_account() -> Rc> { + AccountSharedData::new_ref(0, 0, &Pubkey::new_unique()) } fn process_instruction( - owner: &Pubkey, instruction_data: &[u8], - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); - let mut keyed_accounts = keyed_accounts.to_vec(); - keyed_accounts.insert(0, (false, false, owner, &processor_account)); - super::process_instruction( - 1, + message_processor::mock_process_instruction( + &id(), + Vec::new(), instruction_data, - &mut MockInvokeContext::new(owner, create_keyed_accounts_unified(&keyed_accounts)), + keyed_accounts, + super::process_instruction, ) } - #[allow(clippy::same_item_push)] fn process_instruction_as_one_arg(instruction: &Instruction) -> Result<(), InstructionError> { let mut accounts: Vec<_> = instruction .accounts .iter() .map(|meta| { - RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { + Rc::new(RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { account::create_account_shared_data_for_test(&Clock::default()) } else if sysvar::slot_hashes::check_id(&meta.pubkey) { account::create_account_shared_data_for_test(&SlotHashes::default()) @@ -455,22 +451,26 @@ mod tests { owner: id(), ..Account::default() }) - }) + })) }) .collect(); - + #[allow(clippy::same_item_push)] for _ in 0..instruction.accounts.len() { - accounts.push(RefCell::new(AccountSharedData::default())); - } - { - let keyed_accounts: Vec<_> = instruction - .accounts - .iter() - .zip(accounts.iter()) - .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) - .collect(); - process_instruction(&Pubkey::default(), &instruction.data, &keyed_accounts) + accounts.push(AccountSharedData::new_ref(0, 0, &Pubkey::new_unique())); } + let keyed_accounts: Vec<_> = instruction + .accounts + .iter() + .zip(accounts.into_iter()) + .map(|(meta, account)| (meta.is_signer, meta.is_writable, meta.pubkey, account)) + .collect(); + message_processor::mock_process_instruction( + &id(), + Vec::new(), + &instruction.data, + &keyed_accounts, + super::process_instruction, + ) } fn invalid_vote_state_pubkey() -> Pubkey { @@ -481,7 +481,7 @@ mod tests { #[test] fn test_vote_process_instruction_decode_bail() { assert_eq!( - process_instruction(&Pubkey::default(), &[], &[]), + process_instruction(&[], &[]), Err(InstructionError::NotEnoughAccountKeys), ); } @@ -491,7 +491,7 @@ mod tests { assert_eq!( process_instruction_as_one_arg(&vote( &invalid_vote_state_pubkey(), - &Pubkey::default(), + &Pubkey::new_unique(), Vote::default(), )), Err(InstructionError::InvalidAccountOwner), @@ -502,8 +502,8 @@ mod tests { fn test_vote_process_instruction() { solana_logger::setup(); let instructions = create_account( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), &VoteInit::default(), 100, ); @@ -513,16 +513,16 @@ mod tests { ); assert_eq!( process_instruction_as_one_arg(&vote( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), Vote::default(), )), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction_as_one_arg(&vote_switch( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), Vote::default(), Hash::default(), )), @@ -530,25 +530,25 @@ mod tests { ); assert_eq!( process_instruction_as_one_arg(&authorize( - &Pubkey::default(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), VoteAuthorize::Voter, )), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction_as_one_arg(&update_validator_identity( - &Pubkey::default(), - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), )), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction_as_one_arg(&update_commission( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 0, )), Err(InstructionError::InvalidAccountData), @@ -556,10 +556,10 @@ mod tests { assert_eq!( process_instruction_as_one_arg(&withdraw( - &Pubkey::default(), - &Pubkey::default(), + &Pubkey::new_unique(), + &Pubkey::new_unique(), 0, - &Pubkey::default() + &Pubkey::new_unique() )), Err(InstructionError::InvalidAccountData), ); @@ -624,21 +624,20 @@ mod tests { // Test with new_authorized_pubkey signer let vote_account = AccountSharedData::new_ref(100, VoteState::size_of(), &id()); let clock_address = sysvar::clock::id(); - let clock_account = RefCell::new(account::create_account_shared_data_for_test( + let clock_account = Rc::new(RefCell::new(account::create_account_shared_data_for_test( &Clock::default(), - )); + ))); let default_authorized_pubkey = Pubkey::default(); let authorized_account = create_default_account(); let new_authorized_account = create_default_account(); - let keyed_accounts: [(bool, bool, &Pubkey, &RefCell); 4] = [ - (false, false, &vote_pubkey, &vote_account), - (false, false, &clock_address, &clock_account), - (true, false, &default_authorized_pubkey, &authorized_account), - (true, false, &new_authorized_pubkey, &new_authorized_account), + let keyed_accounts = [ + (false, false, vote_pubkey, vote_account), + (false, false, clock_address, clock_account), + (true, false, default_authorized_pubkey, authorized_account), + (true, false, new_authorized_pubkey, new_authorized_account), ]; assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&VoteInstruction::AuthorizeChecked(VoteAuthorize::Voter)).unwrap(), &keyed_accounts, ), @@ -647,7 +646,6 @@ mod tests { assert_eq!( process_instruction( - &Pubkey::default(), &serialize(&VoteInstruction::AuthorizeChecked( VoteAuthorize::Withdrawer )) From 4064ae942c0a095834420a07a07ddd27ab934d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 2 Nov 2021 14:30:41 +0100 Subject: [PATCH 7/9] Finally, removes MockInvokeContext, MockComputeMeter and MockLogger. --- sdk/src/process_instruction.rs | 204 +-------------------------------- 1 file changed, 2 insertions(+), 202 deletions(-) diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 2f146d0a86fc0f..5924105bcab52f 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -4,15 +4,14 @@ use itertools::Itertools; use solana_sdk::{ account::AccountSharedData, compute_budget::ComputeBudget, - feature_set::remove_native_loader, hash::Hash, instruction::{CompiledInstruction, Instruction, InstructionError}, - keyed_account::{create_keyed_accounts_unified, KeyedAccount}, + keyed_account::KeyedAccount, message::Message, pubkey::Pubkey, sysvar::Sysvar, }; -use std::{cell::RefCell, collections::HashSet, fmt::Debug, rc::Rc, sync::Arc}; +use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc}; /// Prototype of a native loader entry point /// @@ -318,202 +317,3 @@ pub trait Executor: Debug + Send + Sync { use_jit: bool, ) -> Result<(), InstructionError>; } - -#[derive(Debug, Default, Clone)] -pub struct MockComputeMeter { - pub remaining: u64, -} -impl ComputeMeter for MockComputeMeter { - fn consume(&mut self, amount: u64) -> Result<(), InstructionError> { - let exceeded = self.remaining < amount; - self.remaining = self.remaining.saturating_sub(amount); - if exceeded { - return Err(InstructionError::ComputationalBudgetExceeded); - } - Ok(()) - } - fn get_remaining(&self) -> u64 { - self.remaining - } -} - -#[derive(Debug, Default, Clone)] -pub struct MockLogger { - pub log: Rc>>, -} -impl Logger for MockLogger { - fn log_enabled(&self) -> bool { - true - } - fn log(&self, message: &str) { - self.log.borrow_mut().push(message.to_string()); - } -} - -#[allow(deprecated)] -pub struct MockInvokeContext<'a> { - pub invoke_stack: Vec>, - pub logger: MockLogger, - pub compute_budget: ComputeBudget, - pub compute_meter: Rc>, - pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>, - pub accounts: Vec<(Pubkey, Rc>)>, - pub sysvars: &'a [(Pubkey, Vec)], - pub disabled_features: HashSet, - pub blockhash: Hash, - pub lamports_per_signature: u64, - pub return_data: (Pubkey, Vec), -} - -impl<'a> MockInvokeContext<'a> { - pub fn new(program_id: &Pubkey, keyed_accounts: Vec>) -> Self { - let compute_budget = ComputeBudget::default(); - let mut invoke_context = MockInvokeContext { - invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth), - logger: MockLogger::default(), - compute_budget, - compute_meter: Rc::new(RefCell::new(MockComputeMeter { - remaining: std::i64::MAX as u64, - })), - programs: vec![], - accounts: vec![], - sysvars: &[], - disabled_features: HashSet::default(), - blockhash: Hash::default(), - lamports_per_signature: 0, - return_data: (Pubkey::default(), Vec::new()), - }; - let number_of_program_accounts = keyed_accounts - .iter() - .position(|keyed_account| keyed_account.unsigned_key() == program_id) - .unwrap_or(0) - .saturating_add(1); - invoke_context - .invoke_stack - .push(InvokeContextStackFrame::new( - number_of_program_accounts, - keyed_accounts, - )); - invoke_context - } -} - -impl<'a> InvokeContext for MockInvokeContext<'a> { - fn push( - &mut self, - _message: &Message, - _instruction: &CompiledInstruction, - _program_indices: &[usize], - _account_indices: Option<&[usize]>, - ) -> Result<(), InstructionError> { - self.invoke_stack.push(InvokeContextStackFrame::new( - 0, - create_keyed_accounts_unified(&[]), - )); - Ok(()) - } - fn pop(&mut self) { - self.invoke_stack.pop(); - } - fn invoke_depth(&self) -> usize { - self.invoke_stack.len() - } - fn verify( - &mut self, - _message: &Message, - _instruction: &CompiledInstruction, - _program_indices: &[usize], - ) -> Result<(), InstructionError> { - Ok(()) - } - fn verify_and_update( - &mut self, - _instruction: &CompiledInstruction, - _account_indices: &[usize], - _write_pivileges: &[bool], - ) -> Result<(), InstructionError> { - Ok(()) - } - fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - self.invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth) - } - fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { - if !self.is_feature_active(&remove_native_loader::id()) { - let stack_frame = &mut self - .invoke_stack - .last_mut() - .ok_or(InstructionError::CallDepth)?; - stack_frame.keyed_accounts_range.start = - stack_frame.keyed_accounts_range.start.saturating_add(1); - } - Ok(()) - } - fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { - self.invoke_stack - .last() - .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()]) - .ok_or(InstructionError::CallDepth) - } - fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] { - &self.programs - } - fn get_logger(&self) -> Rc> { - Rc::new(RefCell::new(self.logger.clone())) - } - fn get_compute_meter(&self) -> Rc> { - self.compute_meter.clone() - } - fn add_executor(&self, _pubkey: &Pubkey, _executor: Arc) {} - fn get_executor(&self, _pubkey: &Pubkey) -> Option> { - None - } - fn set_instruction_index(&mut self, _instruction_index: usize) {} - fn record_instruction(&self, _instruction: &Instruction) {} - fn is_feature_active(&self, feature_id: &Pubkey) -> bool { - !self.disabled_features.contains(feature_id) - } - fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { - for (index, (key, account)) in self.accounts.iter().enumerate().rev() { - if key == pubkey { - return Some((index, account.clone())); - } - } - None - } - fn update_timing( - &mut self, - _serialize_us: u64, - _create_vm_us: u64, - _execute_us: u64, - _deserialize_us: u64, - ) { - } - fn get_sysvars(&self) -> &[(Pubkey, Vec)] { - self.sysvars - } - fn get_compute_budget(&self) -> &ComputeBudget { - &self.compute_budget - } - fn set_blockhash(&mut self, hash: Hash) { - self.blockhash = hash; - } - fn get_blockhash(&self) -> &Hash { - &self.blockhash - } - fn set_lamports_per_signature(&mut self, lamports_per_signature: u64) { - self.lamports_per_signature = lamports_per_signature; - } - fn get_lamports_per_signature(&self) -> u64 { - self.lamports_per_signature - } - fn set_return_data(&mut self, data: Vec) -> Result<(), InstructionError> { - self.return_data = (*self.get_caller()?, data); - Ok(()) - } - fn get_return_data(&self) -> (Pubkey, &[u8]) { - (self.return_data.0, &self.return_data.1) - } -} From 868ddc0278f8ead05045c07a7e2f029f012f067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 3 Nov 2021 18:50:06 +0100 Subject: [PATCH 8/9] Adjusts assert_instruction_count test. --- programs/bpf/tests/programs.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 01e3edc5bb3b35..19f507d91f52b0 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1371,8 +1371,8 @@ fn assert_instruction_count() { ("noop++", 5), ("relative_call", 26), ("return_data", 980), - ("sanity", 1246), - ("sanity++", 1250), + ("sanity", 1248), + ("sanity++", 1252), ("secp256k1_recover", 25383), ("sha", 1328), ("struct_pass", 108), @@ -1384,17 +1384,17 @@ fn assert_instruction_count() { programs.extend_from_slice(&[ ("solana_bpf_rust_128bit", 584), ("solana_bpf_rust_alloc", 7388), - ("solana_bpf_rust_custom_heap", 535), + ("solana_bpf_rust_custom_heap", 536), ("solana_bpf_rust_dep_crate", 47), - ("solana_bpf_rust_external_spend", 506), + ("solana_bpf_rust_external_spend", 507), ("solana_bpf_rust_iter", 824), ("solana_bpf_rust_many_args", 941), - ("solana_bpf_rust_mem", 3085), + ("solana_bpf_rust_mem", 3086), ("solana_bpf_rust_membuiltins", 3976), - ("solana_bpf_rust_noop", 480), + ("solana_bpf_rust_noop", 481), ("solana_bpf_rust_param_passing", 146), - ("solana_bpf_rust_rand", 487), - ("solana_bpf_rust_sanity", 8454), + ("solana_bpf_rust_rand", 488), + ("solana_bpf_rust_sanity", 8455), ("solana_bpf_rust_secp256k1_recover", 25216), ("solana_bpf_rust_sha", 30692), ]); From 9c29e444c62d9cf764c054bac813fa37572fb7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 3 Nov 2021 11:47:33 +0100 Subject: [PATCH 9/9] Moves ThisInvokeContext to the program-runtime crate. --- Cargo.lock | 11 +- cli/Cargo.toml | 2 +- cli/src/program.rs | 2 +- core/src/cost_update_service.rs | 2 +- program-runtime/Cargo.toml | 1 + .../benches/instruction_processor.rs | 2 +- .../src/instruction_recorder.rs | 0 program-runtime/src/invoke_context.rs | 1080 +++++++++++++++++ program-runtime/src/lib.rs | 12 +- .../src/log_collector.rs | 0 program-test/src/lib.rs | 6 +- programs/bpf/Cargo.lock | 3 + programs/bpf/Cargo.toml | 1 + programs/bpf/benches/bpf_loader.rs | 2 +- programs/bpf/tests/programs.rs | 2 +- programs/bpf_loader/src/lib.rs | 9 +- programs/bpf_loader/src/syscalls.rs | 14 +- programs/config/Cargo.toml | 2 +- programs/config/src/config_processor.rs | 4 +- programs/stake/Cargo.toml | 2 +- programs/stake/src/stake_instruction.rs | 5 +- programs/stake/src/stake_state.rs | 2 +- programs/vote/Cargo.toml | 2 +- programs/vote/src/vote_instruction.rs | 6 +- rbpf-cli/Cargo.toml | 2 +- rbpf-cli/src/main.rs | 2 +- runtime/src/bank.rs | 10 +- runtime/src/lib.rs | 2 - runtime/src/message_processor.rs | 1029 +--------------- runtime/src/nonce_keyed_account.rs | 2 +- runtime/src/serde_snapshot.rs | 2 +- runtime/src/system_instruction_processor.rs | 7 +- sdk/src/process_instruction.rs | 47 - 33 files changed, 1163 insertions(+), 1112 deletions(-) rename {runtime => program-runtime}/src/instruction_recorder.rs (100%) create mode 100644 program-runtime/src/invoke_context.rs rename {runtime => program-runtime}/src/log_collector.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4d58261b5ab7e9..2d3ac485753d28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3598,7 +3598,7 @@ dependencies = [ "serde_json", "solana-bpf-loader-program", "solana-logger 1.9.0", - "solana-runtime", + "solana-program-runtime", "solana-sdk", "solana_rbpf", "time 0.3.4", @@ -4542,8 +4542,8 @@ dependencies = [ "solana-config-program", "solana-faucet", "solana-logger 1.9.0", + "solana-program-runtime", "solana-remote-wallet", - "solana-runtime", "solana-sdk", "solana-streamer", "solana-test-validator", @@ -4664,7 +4664,7 @@ dependencies = [ "serde", "serde_derive", "solana-logger 1.9.0", - "solana-runtime", + "solana-program-runtime", "solana-sdk", ] @@ -5380,6 +5380,7 @@ dependencies = [ name = "solana-program-runtime" version = "1.9.0" dependencies = [ + "bincode", "libc", "libloading", "log 0.4.14", @@ -5733,7 +5734,7 @@ dependencies = [ "solana-frozen-abi-macro 1.9.0", "solana-logger 1.9.0", "solana-metrics", - "solana-runtime", + "solana-program-runtime", "solana-sdk", "solana-vote-program", "thiserror", @@ -6004,7 +6005,7 @@ dependencies = [ "solana-frozen-abi-macro 1.9.0", "solana-logger 1.9.0", "solana-metrics", - "solana-runtime", + "solana-program-runtime", "solana-sdk", "thiserror", ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4fbde8a6d44d3f..1099d456c057d7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -35,9 +35,9 @@ solana-client = { path = "../client", version = "=1.9.0" } solana-config-program = { path = "../programs/config", version = "=1.9.0" } solana-faucet = { path = "../faucet", version = "=1.9.0" } solana-logger = { path = "../logger", version = "=1.9.0" } +solana-program-runtime = { path = "../program-runtime", version = "=1.9.0" } solana_rbpf = "=0.2.14" solana-remote-wallet = { path = "../remote-wallet", version = "=1.9.0" } -solana-runtime = { path = "../runtime", version = "=1.9.0" } solana-sdk = { path = "../sdk", version = "=1.9.0" } solana-transaction-status = { path = "../transaction-status", version = "=1.9.0" } solana-version = { path = "../version", version = "=1.9.0" } diff --git a/cli/src/program.rs b/cli/src/program.rs index 0f8cacb4ff9ee6..6b2fa52c6c2aa3 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -24,12 +24,12 @@ use solana_client::{ rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, tpu_client::{TpuClient, TpuClientConfig}, }; +use solana_program_runtime::invoke_context::ThisInvokeContext; use solana_rbpf::{ verifier, vm::{Config, Executable}, }; use solana_remote_wallet::remote_wallet::RemoteWalletManager; -use solana_runtime::message_processor::ThisInvokeContext; use solana_sdk::{ account::Account, account_utils::StateMut, diff --git a/core/src/cost_update_service.rs b/core/src/cost_update_service.rs index 2522d7975a8344..44d51d87efce5f 100644 --- a/core/src/cost_update_service.rs +++ b/core/src/cost_update_service.rs @@ -204,7 +204,7 @@ impl CostUpdateService { #[cfg(test)] mod tests { use super::*; - use solana_program_runtime::ProgramTiming; + use solana_program_runtime::instruction_processor::ProgramTiming; use solana_sdk::pubkey::Pubkey; #[test] diff --git a/program-runtime/Cargo.toml b/program-runtime/Cargo.toml index 5659ef9ce968c5..afcacaf148d273 100644 --- a/program-runtime/Cargo.toml +++ b/program-runtime/Cargo.toml @@ -10,6 +10,7 @@ documentation = "https://docs.rs/solana-program-runtime" edition = "2018" [dependencies] +bincode = "1.3.3" libc = "0.2.101" libloading = "0.7.0" log = "0.4.14" diff --git a/program-runtime/benches/instruction_processor.rs b/program-runtime/benches/instruction_processor.rs index 5de050e8069926..b5f8f368bf6d32 100644 --- a/program-runtime/benches/instruction_processor.rs +++ b/program-runtime/benches/instruction_processor.rs @@ -3,7 +3,7 @@ extern crate test; use log::*; -use solana_program_runtime::{ExecuteDetailsTimings, PreAccount}; +use solana_program_runtime::instruction_processor::{ExecuteDetailsTimings, PreAccount}; use solana_sdk::{account::AccountSharedData, pubkey, rent::Rent}; use test::Bencher; diff --git a/runtime/src/instruction_recorder.rs b/program-runtime/src/instruction_recorder.rs similarity index 100% rename from runtime/src/instruction_recorder.rs rename to program-runtime/src/instruction_recorder.rs diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs new file mode 100644 index 00000000000000..357ff1a741fe59 --- /dev/null +++ b/program-runtime/src/invoke_context.rs @@ -0,0 +1,1080 @@ +use crate::{ + instruction_processor::{ExecuteDetailsTimings, Executors, PreAccount}, + instruction_recorder::InstructionRecorder, + log_collector::LogCollector, +}; +use log::*; +use solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + compute_budget::ComputeBudget, + feature_set::{ + demote_program_write_locks, do_support_realloc, remove_native_loader, tx_wide_compute_cap, + FeatureSet, + }, + hash::Hash, + ic_logger_msg, ic_msg, + instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, + keyed_account::{create_keyed_accounts_unified, KeyedAccount}, + message::Message, + process_instruction::{ + ComputeMeter, Executor, InvokeContext, Logger, ProcessInstructionWithContext, + }, + pubkey::Pubkey, + rent::Rent, + sysvar::Sysvar, +}; +use std::{cell::RefCell, rc::Rc, sync::Arc}; + +pub struct ThisComputeMeter { + remaining: u64, +} +impl ComputeMeter for ThisComputeMeter { + fn consume(&mut self, amount: u64) -> Result<(), InstructionError> { + let exceeded = self.remaining < amount; + self.remaining = self.remaining.saturating_sub(amount); + if exceeded { + return Err(InstructionError::ComputationalBudgetExceeded); + } + Ok(()) + } + fn get_remaining(&self) -> u64 { + self.remaining + } +} +impl ThisComputeMeter { + pub fn new_ref(remaining: u64) -> Rc> { + Rc::new(RefCell::new(Self { remaining })) + } +} + +pub struct ThisLogger { + log_collector: Option>, +} +impl Logger for ThisLogger { + fn log_enabled(&self) -> bool { + log_enabled!(log::Level::Info) || self.log_collector.is_some() + } + fn log(&self, message: &str) { + debug!("{}", message); + if let Some(log_collector) = &self.log_collector { + log_collector.log(message); + } + } +} +impl ThisLogger { + pub fn new_ref(log_collector: Option>) -> Rc> { + Rc::new(RefCell::new(Self { log_collector })) + } +} + +pub struct InvokeContextStackFrame<'a> { + pub number_of_program_accounts: usize, + pub keyed_accounts: Vec>, + pub keyed_accounts_range: std::ops::Range, +} + +impl<'a> InvokeContextStackFrame<'a> { + pub fn new(number_of_program_accounts: usize, keyed_accounts: Vec>) -> Self { + let keyed_accounts_range = std::ops::Range { + start: 0, + end: keyed_accounts.len(), + }; + Self { + number_of_program_accounts, + keyed_accounts, + keyed_accounts_range, + } + } + + pub fn program_id(&self) -> Option<&Pubkey> { + self.keyed_accounts + .get(self.number_of_program_accounts.saturating_sub(1)) + .map(|keyed_account| keyed_account.unsigned_key()) + } +} + +pub struct ThisInvokeContext<'a> { + instruction_index: usize, + invoke_stack: Vec>, + rent: Rent, + pre_accounts: Vec, + accounts: &'a [(Pubkey, Rc>)], + programs: &'a [(Pubkey, ProcessInstructionWithContext)], + sysvars: &'a [(Pubkey, Vec)], + logger: Rc>, + compute_budget: ComputeBudget, + compute_meter: Rc>, + executors: Rc>, + instruction_recorders: Option<&'a [InstructionRecorder]>, + feature_set: Arc, + pub timings: ExecuteDetailsTimings, + blockhash: Hash, + lamports_per_signature: u64, + return_data: (Pubkey, Vec), +} +impl<'a> ThisInvokeContext<'a> { + #[allow(clippy::too_many_arguments)] + pub fn new( + rent: Rent, + accounts: &'a [(Pubkey, Rc>)], + programs: &'a [(Pubkey, ProcessInstructionWithContext)], + sysvars: &'a [(Pubkey, Vec)], + log_collector: Option>, + compute_budget: ComputeBudget, + compute_meter: Rc>, + executors: Rc>, + instruction_recorders: Option<&'a [InstructionRecorder]>, + feature_set: Arc, + blockhash: Hash, + lamports_per_signature: u64, + ) -> Self { + Self { + instruction_index: 0, + invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth), + rent, + pre_accounts: Vec::new(), + accounts, + programs, + sysvars, + logger: ThisLogger::new_ref(log_collector), + compute_budget, + compute_meter, + executors, + instruction_recorders, + feature_set, + timings: ExecuteDetailsTimings::default(), + blockhash, + lamports_per_signature, + return_data: (Pubkey::default(), Vec::new()), + } + } + + pub fn new_mock_with_sysvars_and_features( + accounts: &'a [(Pubkey, Rc>)], + programs: &'a [(Pubkey, ProcessInstructionWithContext)], + sysvars: &'a [(Pubkey, Vec)], + feature_set: Arc, + ) -> Self { + Self::new( + Rent::default(), + accounts, + programs, + sysvars, + None, + ComputeBudget::default(), + ThisComputeMeter::new_ref(std::i64::MAX as u64), + Rc::new(RefCell::new(Executors::default())), + None, + feature_set, + Hash::default(), + 0, + ) + } + + pub fn new_mock( + accounts: &'a [(Pubkey, Rc>)], + programs: &'a [(Pubkey, ProcessInstructionWithContext)], + ) -> Self { + Self::new_mock_with_sysvars_and_features( + accounts, + programs, + &[], + Arc::new(FeatureSet::all_enabled()), + ) + } +} +impl<'a> InvokeContext for ThisInvokeContext<'a> { + fn push( + &mut self, + message: &Message, + instruction: &CompiledInstruction, + program_indices: &[usize], + account_indices: Option<&[usize]>, + ) -> Result<(), InstructionError> { + if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { + return Err(InstructionError::CallDepth); + } + + if let Some(index_of_program_id) = program_indices.last() { + let program_id = &self.accounts[*index_of_program_id].0; + let contains = self + .invoke_stack + .iter() + .any(|frame| frame.program_id() == Some(program_id)); + let is_last = if let Some(last_frame) = self.invoke_stack.last() { + last_frame.program_id() == Some(program_id) + } else { + false + }; + if contains && !is_last { + // Reentrancy not allowed unless caller is calling itself + return Err(InstructionError::ReentrancyNotAllowed); + } + } + + if self.invoke_stack.is_empty() { + if !self.feature_set.is_active(&tx_wide_compute_cap::id()) { + self.compute_meter = ThisComputeMeter::new_ref(self.compute_budget.max_units); + } + + self.pre_accounts = Vec::with_capacity(instruction.accounts.len()); + let mut work = |_unique_index: usize, account_index: usize| { + if account_index < self.accounts.len() { + let account = self.accounts[account_index].1.borrow(); + self.pre_accounts + .push(PreAccount::new(&self.accounts[account_index].0, &account)); + return Ok(()); + } + Err(InstructionError::MissingAccount) + }; + instruction.visit_each_account(&mut work)?; + } + + // Create the KeyedAccounts that will be passed to the program + let demote_program_write_locks = self + .feature_set + .is_active(&demote_program_write_locks::id()); + let keyed_accounts = program_indices + .iter() + .map(|account_index| { + ( + false, + false, + &self.accounts[*account_index].0, + &self.accounts[*account_index].1 as &RefCell, + ) + }) + .chain(instruction.accounts.iter().map(|index_in_instruction| { + let index_in_instruction = *index_in_instruction as usize; + let account_index = if let Some(account_indices) = account_indices { + account_indices[index_in_instruction] + } else { + index_in_instruction + }; + ( + message.is_signer(index_in_instruction), + message.is_writable(index_in_instruction, demote_program_write_locks), + &self.accounts[account_index].0, + &self.accounts[account_index].1 as &RefCell, + ) + })) + .collect::>(); + + self.invoke_stack.push(InvokeContextStackFrame::new( + program_indices.len(), + create_keyed_accounts_unified(keyed_accounts.as_slice()), + )); + Ok(()) + } + fn pop(&mut self) { + self.invoke_stack.pop(); + } + fn invoke_depth(&self) -> usize { + self.invoke_stack.len() + } + fn verify( + &mut self, + message: &Message, + instruction: &CompiledInstruction, + program_indices: &[usize], + ) -> Result<(), InstructionError> { + let program_id = instruction.program_id(&message.account_keys); + let demote_program_write_locks = self.is_feature_active(&demote_program_write_locks::id()); + let do_support_realloc = self.is_feature_active(&do_support_realloc::id()); + + // Verify all executable accounts have zero outstanding refs + for account_index in program_indices.iter() { + self.accounts[*account_index] + .1 + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowOutstanding)?; + } + + // Verify the per-account instruction results + let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); + let mut work = |unique_index: usize, account_index: usize| { + { + // Verify account has no outstanding references + let _ = self.accounts[account_index] + .1 + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowOutstanding)?; + } + let account = self.accounts[account_index].1.borrow(); + self.pre_accounts[unique_index] + .verify( + program_id, + message.is_writable(account_index, demote_program_write_locks), + &self.rent, + &account, + &mut self.timings, + true, + do_support_realloc, + ) + .map_err(|err| { + ic_logger_msg!( + self.logger, + "failed to verify account {}: {}", + self.pre_accounts[unique_index].key(), + err + ); + err + })?; + pre_sum += u128::from(self.pre_accounts[unique_index].lamports()); + post_sum += u128::from(account.lamports()); + Ok(()) + }; + instruction.visit_each_account(&mut work)?; + + // Verify that the total sum of all the lamports did not change + if pre_sum != post_sum { + return Err(InstructionError::UnbalancedInstruction); + } + Ok(()) + } + fn verify_and_update( + &mut self, + instruction: &CompiledInstruction, + account_indices: &[usize], + write_privileges: &[bool], + ) -> Result<(), InstructionError> { + let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); + let program_id = self + .invoke_stack + .last() + .and_then(|frame| frame.program_id()) + .ok_or(InstructionError::CallDepth)?; + let rent = &self.rent; + let logger = &self.logger; + let accounts = &self.accounts; + let pre_accounts = &mut self.pre_accounts; + let timings = &mut self.timings; + + // Verify the per-account instruction results + let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); + let mut work = |_unique_index: usize, index_in_instruction: usize| { + if index_in_instruction < write_privileges.len() + && index_in_instruction < account_indices.len() + { + let account_index = account_indices[index_in_instruction]; + let (key, account) = &accounts[account_index]; + let is_writable = write_privileges[index_in_instruction]; + // Find the matching PreAccount + for pre_account in pre_accounts.iter_mut() { + if key == pre_account.key() { + { + // Verify account has no outstanding references + let _ = account + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowOutstanding)?; + } + let account = account.borrow(); + pre_account + .verify( + program_id, + is_writable, + rent, + &account, + timings, + false, + do_support_realloc, + ) + .map_err(|err| { + ic_logger_msg!(logger, "failed to verify account {}: {}", key, err); + err + })?; + pre_sum += u128::from(pre_account.lamports()); + post_sum += u128::from(account.lamports()); + if is_writable && !pre_account.executable() { + pre_account.update(&account); + } + return Ok(()); + } + } + } + Err(InstructionError::MissingAccount) + }; + instruction.visit_each_account(&mut work)?; + + // Verify that the total sum of all the lamports did not change + if pre_sum != post_sum { + return Err(InstructionError::UnbalancedInstruction); + } + Ok(()) + } + fn get_caller(&self) -> Result<&Pubkey, InstructionError> { + self.invoke_stack + .last() + .and_then(|frame| frame.program_id()) + .ok_or(InstructionError::CallDepth) + } + fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { + if !self.is_feature_active(&remove_native_loader::id()) { + let stack_frame = &mut self + .invoke_stack + .last_mut() + .ok_or(InstructionError::CallDepth)?; + stack_frame.keyed_accounts_range.start = + stack_frame.keyed_accounts_range.start.saturating_add(1); + } + Ok(()) + } + fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { + self.invoke_stack + .last() + .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()]) + .ok_or(InstructionError::CallDepth) + } + fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] { + self.programs + } + fn get_logger(&self) -> Rc> { + self.logger.clone() + } + fn get_compute_meter(&self) -> Rc> { + self.compute_meter.clone() + } + fn add_executor(&self, pubkey: &Pubkey, executor: Arc) { + self.executors.borrow_mut().insert(*pubkey, executor); + } + fn get_executor(&self, pubkey: &Pubkey) -> Option> { + self.executors.borrow().get(pubkey) + } + fn set_instruction_index(&mut self, instruction_index: usize) { + self.instruction_index = instruction_index; + } + fn record_instruction(&self, instruction: &Instruction) { + if let Some(instruction_recorders) = &self.instruction_recorders { + instruction_recorders[self.instruction_index].record_instruction(instruction.clone()); + } + } + fn is_feature_active(&self, feature_id: &Pubkey) -> bool { + self.feature_set.is_active(feature_id) + } + fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { + for (index, (key, account)) in self.accounts.iter().enumerate().rev() { + if key == pubkey { + return Some((index, account.clone())); + } + } + None + } + fn update_timing( + &mut self, + serialize_us: u64, + create_vm_us: u64, + execute_us: u64, + deserialize_us: u64, + ) { + self.timings.serialize_us += serialize_us; + self.timings.create_vm_us += create_vm_us; + self.timings.execute_us += execute_us; + self.timings.deserialize_us += deserialize_us; + } + fn get_sysvars(&self) -> &[(Pubkey, Vec)] { + self.sysvars + } + fn get_compute_budget(&self) -> &ComputeBudget { + &self.compute_budget + } + fn set_blockhash(&mut self, hash: Hash) { + self.blockhash = hash; + } + fn get_blockhash(&self) -> &Hash { + &self.blockhash + } + fn set_lamports_per_signature(&mut self, lamports_per_signature: u64) { + self.lamports_per_signature = lamports_per_signature; + } + fn get_lamports_per_signature(&self) -> u64 { + self.lamports_per_signature + } + fn set_return_data(&mut self, data: Vec) -> Result<(), InstructionError> { + self.return_data = (*self.get_caller()?, data); + Ok(()) + } + fn get_return_data(&self) -> (Pubkey, &[u8]) { + (self.return_data.0, &self.return_data.1) + } +} + +// This method which has a generic parameter is outside of the InvokeContext, +// because the InvokeContext is a dyn Trait. +pub fn get_sysvar( + invoke_context: &dyn InvokeContext, + id: &Pubkey, +) -> Result { + invoke_context + .get_sysvars() + .iter() + .find_map(|(key, data)| { + if id == key { + bincode::deserialize(data).ok() + } else { + None + } + }) + .ok_or_else(|| { + ic_msg!(invoke_context, "Unable to get sysvar {}", id); + InstructionError::UnsupportedSysvar + }) +} + +pub struct MockInvokeContextPreparation { + pub accounts: Vec<(Pubkey, Rc>)>, + pub message: Message, + pub account_indices: Vec, +} + +pub fn prepare_mock_invoke_context( + program_indices: &[usize], + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], +) -> MockInvokeContextPreparation { + #[allow(clippy::type_complexity)] + let (accounts, mut metas): ( + Vec<(Pubkey, Rc>)>, + Vec, + ) = keyed_accounts + .iter() + .map(|(is_signer, is_writable, pubkey, account)| { + ( + (*pubkey, account.clone()), + AccountMeta { + pubkey: *pubkey, + is_signer: *is_signer, + is_writable: *is_writable, + }, + ) + }) + .unzip(); + let program_id = if let Some(program_index) = program_indices.last() { + accounts[*program_index].0 + } else { + Pubkey::default() + }; + for program_index in program_indices.iter().rev() { + metas.remove(*program_index); + } + let message = Message::new( + &[Instruction::new_with_bytes( + program_id, + instruction_data, + metas, + )], + None, + ); + let account_indices: Vec = message + .account_keys + .iter() + .map(|search_key| { + accounts + .iter() + .position(|(key, _account)| key == search_key) + .unwrap_or(accounts.len()) + }) + .collect(); + MockInvokeContextPreparation { + accounts, + message, + account_indices, + } +} + +pub fn with_mock_invoke_context R>( + loader_id: Pubkey, + account_size: usize, + mut callback: F, +) -> R { + let program_indices = vec![0, 1]; + let keyed_accounts = [ + ( + false, + false, + loader_id, + AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()), + ), + ( + false, + false, + Pubkey::new_unique(), + AccountSharedData::new_ref(1, 0, &loader_id), + ), + ( + false, + false, + Pubkey::new_unique(), + AccountSharedData::new_ref(2, account_size, &Pubkey::new_unique()), + ), + ]; + let preparation = prepare_mock_invoke_context(&program_indices, &[], &keyed_accounts); + let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); + invoke_context + .push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + ) + .unwrap(); + callback(&mut invoke_context) +} + +pub fn mock_process_instruction( + loader_id: &Pubkey, + mut program_indices: Vec, + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, Pubkey, Rc>)], + process_instruction: ProcessInstructionWithContext, +) -> Result<(), InstructionError> { + let mut preparation = + prepare_mock_invoke_context(&program_indices, instruction_data, keyed_accounts); + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + program_indices.insert(0, preparation.accounts.len()); + preparation.accounts.push((*loader_id, processor_account)); + let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); + invoke_context.push( + &preparation.message, + &preparation.message.instructions[0], + &program_indices, + Some(&preparation.account_indices), + )?; + process_instruction(1, instruction_data, &mut invoke_context) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instruction_processor::InstructionProcessor; + use serde::{Deserialize, Serialize}; + use solana_sdk::{ + account::{ReadableAccount, WritableAccount}, + instruction::{AccountMeta, Instruction, InstructionError}, + keyed_account::keyed_account_at_index, + message::Message, + native_loader, + }; + + #[derive(Debug, Serialize, Deserialize)] + enum MockInstruction { + NoopSuccess, + NoopFail, + ModifyOwned, + ModifyNotOwned, + ModifyReadonly, + } + + fn mock_process_instruction( + first_instruction_account: usize, + data: &[u8], + invoke_context: &mut dyn InvokeContext, + ) -> Result<(), InstructionError> { + let program_id = invoke_context.get_caller()?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + assert_eq!( + *program_id, + keyed_account_at_index(keyed_accounts, first_instruction_account)?.owner()? + ); + assert_ne!( + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.owner()?, + *keyed_account_at_index(keyed_accounts, first_instruction_account)?.unsigned_key() + ); + + if let Ok(instruction) = bincode::deserialize(data) { + match instruction { + MockInstruction::NoopSuccess => (), + MockInstruction::NoopFail => return Err(InstructionError::GenericError), + MockInstruction::ModifyOwned => { + keyed_account_at_index(keyed_accounts, first_instruction_account)? + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 + } + MockInstruction::ModifyNotOwned => { + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)? + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 + } + MockInstruction::ModifyReadonly => { + keyed_account_at_index(keyed_accounts, first_instruction_account + 2)? + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 + } + } + } else { + return Err(InstructionError::InvalidInstructionData); + } + Ok(()) + } + + #[test] + fn test_invoke_context() { + const MAX_DEPTH: usize = 10; + let mut invoke_stack = vec![]; + let mut accounts = vec![]; + let mut metas = vec![]; + for i in 0..MAX_DEPTH { + invoke_stack.push(solana_sdk::pubkey::new_rand()); + accounts.push(( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(AccountSharedData::new( + i as u64, + 1, + &invoke_stack[i], + ))), + )); + metas.push(AccountMeta::new(accounts[i].0, false)); + } + for program_id in invoke_stack.iter() { + accounts.push(( + *program_id, + Rc::new(RefCell::new(AccountSharedData::new( + 1, + 1, + &solana_sdk::pubkey::Pubkey::default(), + ))), + )); + metas.push(AccountMeta::new(*program_id, false)); + } + let account_indices = (0..accounts.len()).collect::>(); + + let message = Message::new( + &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)], + None, + ); + let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]); + + // Check call depth increases and has a limit + let mut depth_reached = 0; + for _ in 0..invoke_stack.len() { + if Err(InstructionError::CallDepth) + == invoke_context.push( + &message, + &message.instructions[0], + &[MAX_DEPTH + depth_reached], + None, + ) + { + break; + } + depth_reached += 1; + } + assert_ne!(depth_reached, 0); + assert!(depth_reached < MAX_DEPTH); + + // Mock each invocation + for owned_index in (1..depth_reached).rev() { + let not_owned_index = owned_index - 1; + let metas = vec![ + AccountMeta::new(accounts[not_owned_index].0, false), + AccountMeta::new(accounts[owned_index].0, false), + ]; + let message = Message::new( + &[Instruction::new_with_bytes( + invoke_stack[owned_index], + &[0], + metas, + )], + None, + ); + let write_privileges: Vec = (0..message.account_keys.len()) + .map(|i| message.is_writable(i, /*demote_program_write_locks=*/ true)) + .collect(); + + // modify account owned by the program + accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] = + (MAX_DEPTH + owned_index) as u8; + invoke_context + .verify_and_update( + &message.instructions[0], + &account_indices[not_owned_index..owned_index + 1], + &write_privileges, + ) + .unwrap(); + assert_eq!( + invoke_context.pre_accounts[owned_index].data()[0], + (MAX_DEPTH + owned_index) as u8 + ); + + // modify account not owned by the program + let data = accounts[not_owned_index].1.borrow_mut().data()[0]; + accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = + (MAX_DEPTH + not_owned_index) as u8; + assert_eq!( + invoke_context.verify_and_update( + &message.instructions[0], + &account_indices[not_owned_index..owned_index + 1], + &write_privileges, + ), + Err(InstructionError::ExternalAccountDataModified) + ); + assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data); + accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = data; + + invoke_context.pop(); + } + } + + #[test] + fn test_invoke_context_verify() { + let accounts = vec![( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(AccountSharedData::default())), + )]; + let message = Message::new( + &[Instruction::new_with_bincode( + accounts[0].0, + &MockInstruction::NoopSuccess, + vec![AccountMeta::new_readonly(accounts[0].0, false)], + )], + None, + ); + let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]); + invoke_context + .push(&message, &message.instructions[0], &[0], None) + .unwrap(); + assert!(invoke_context + .verify(&message, &message.instructions[0], &[0]) + .is_ok()); + + let mut _borrowed = accounts[0].1.borrow(); + assert_eq!( + invoke_context.verify(&message, &message.instructions[0], &[0]), + Err(InstructionError::AccountBorrowOutstanding) + ); + } + + #[test] + fn test_process_cross_program() { + let caller_program_id = solana_sdk::pubkey::new_rand(); + let callee_program_id = solana_sdk::pubkey::new_rand(); + + let owned_account = AccountSharedData::new(42, 1, &callee_program_id); + let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); + let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand()); + let loader_account = AccountSharedData::new(0, 0, &native_loader::id()); + let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); + program_account.set_executable(true); + + let accounts = vec![ + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(owned_account)), + ), + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(not_owned_account)), + ), + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(readonly_account)), + ), + (caller_program_id, Rc::new(RefCell::new(loader_account))), + (callee_program_id, Rc::new(RefCell::new(program_account))), + ]; + let account_indices = [0, 1, 2]; + let program_indices = [3, 4]; + + let programs: Vec<(_, ProcessInstructionWithContext)> = + vec![(callee_program_id, mock_process_instruction)]; + let metas = vec![ + AccountMeta::new(accounts[0].0, false), + AccountMeta::new(accounts[1].0, false), + AccountMeta::new_readonly(accounts[2].0, false), + ]; + + let caller_instruction = + CompiledInstruction::new(program_indices[0] as u8, &(), vec![0, 1, 2, 3, 4]); + let callee_instruction = Instruction::new_with_bincode( + callee_program_id, + &MockInstruction::NoopSuccess, + metas.clone(), + ); + let message = Message::new(&[callee_instruction], None); + + let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice()); + invoke_context + .push(&message, &caller_instruction, &program_indices[..1], None) + .unwrap(); + + // not owned account modified by the caller (before the invoke) + let demote_program_write_locks = + invoke_context.is_feature_active(&demote_program_write_locks::id()); + let caller_write_privileges = message + .account_keys + .iter() + .enumerate() + .map(|(i, _)| message.is_writable(i, demote_program_write_locks)) + .collect::>(); + accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1; + assert_eq!( + InstructionProcessor::process_cross_program_instruction( + &message, + &program_indices[1..], + &account_indices, + &caller_write_privileges, + &mut invoke_context, + ), + Err(InstructionError::ExternalAccountDataModified) + ); + accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0; + + // readonly account modified by the invoker + accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; + assert_eq!( + InstructionProcessor::process_cross_program_instruction( + &message, + &program_indices[1..], + &account_indices, + &caller_write_privileges, + &mut invoke_context, + ), + Err(InstructionError::ReadonlyDataModified) + ); + accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; + + invoke_context.pop(); + + let cases = vec![ + (MockInstruction::NoopSuccess, Ok(())), + ( + MockInstruction::NoopFail, + Err(InstructionError::GenericError), + ), + (MockInstruction::ModifyOwned, Ok(())), + ( + MockInstruction::ModifyNotOwned, + Err(InstructionError::ExternalAccountDataModified), + ), + ]; + for case in cases { + let callee_instruction = + Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); + let message = Message::new(&[callee_instruction], None); + invoke_context + .push(&message, &caller_instruction, &program_indices[..1], None) + .unwrap(); + let caller_write_privileges = message + .account_keys + .iter() + .enumerate() + .map(|(i, _)| message.is_writable(i, demote_program_write_locks)) + .collect::>(); + assert_eq!( + InstructionProcessor::process_cross_program_instruction( + &message, + &program_indices[1..], + &account_indices, + &caller_write_privileges, + &mut invoke_context, + ), + case.1 + ); + invoke_context.pop(); + } + } + + #[test] + fn test_native_invoke() { + let caller_program_id = solana_sdk::pubkey::new_rand(); + let callee_program_id = solana_sdk::pubkey::new_rand(); + + let owned_account = AccountSharedData::new(42, 1, &callee_program_id); + let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); + let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand()); + let loader_account = AccountSharedData::new(0, 0, &native_loader::id()); + let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); + program_account.set_executable(true); + + let accounts = vec![ + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(owned_account)), + ), + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(not_owned_account)), + ), + ( + solana_sdk::pubkey::new_rand(), + Rc::new(RefCell::new(readonly_account)), + ), + (caller_program_id, Rc::new(RefCell::new(loader_account))), + (callee_program_id, Rc::new(RefCell::new(program_account))), + ]; + let program_indices = [3]; + let programs: Vec<(_, ProcessInstructionWithContext)> = + vec![(callee_program_id, mock_process_instruction)]; + let metas = vec![ + AccountMeta::new(accounts[0].0, false), + AccountMeta::new(accounts[1].0, false), + AccountMeta::new_readonly(accounts[2].0, false), + ]; + + let caller_instruction = + CompiledInstruction::new(program_indices[0] as u8, &(), vec![0, 1, 2, 3, 4]); + let callee_instruction = Instruction::new_with_bincode( + callee_program_id, + &MockInstruction::NoopSuccess, + metas.clone(), + ); + let message = Message::new(&[callee_instruction.clone()], None); + + let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice()); + invoke_context + .push(&message, &caller_instruction, &program_indices, None) + .unwrap(); + + // not owned account modified by the invoker + accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1; + assert_eq!( + InstructionProcessor::native_invoke( + &mut invoke_context, + callee_instruction.clone(), + &[] + ), + Err(InstructionError::ExternalAccountDataModified) + ); + accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0; + + // readonly account modified by the invoker + accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; + assert_eq!( + InstructionProcessor::native_invoke(&mut invoke_context, callee_instruction, &[]), + Err(InstructionError::ReadonlyDataModified) + ); + accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; + + invoke_context.pop(); + + // Other test cases + let cases = vec![ + (MockInstruction::NoopSuccess, Ok(())), + ( + MockInstruction::NoopFail, + Err(InstructionError::GenericError), + ), + (MockInstruction::ModifyOwned, Ok(())), + ( + MockInstruction::ModifyNotOwned, + Err(InstructionError::ExternalAccountDataModified), + ), + ( + MockInstruction::ModifyReadonly, + Err(InstructionError::ReadonlyDataModified), + ), + ]; + for case in cases { + let callee_instruction = + Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); + let message = Message::new(&[callee_instruction.clone()], None); + invoke_context + .push(&message, &caller_instruction, &program_indices, None) + .unwrap(); + assert_eq!( + InstructionProcessor::native_invoke(&mut invoke_context, callee_instruction, &[]), + case.1 + ); + invoke_context.pop(); + } + } +} diff --git a/program-runtime/src/lib.rs b/program-runtime/src/lib.rs index f2f993cd8da633..b35880ebbc7fa4 100644 --- a/program-runtime/src/lib.rs +++ b/program-runtime/src/lib.rs @@ -1,8 +1,8 @@ #![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(min_specialization))] -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::integer_arithmetic)] // TODO: Remove -mod instruction_processor; -mod native_loader; - -pub use instruction_processor::*; -pub use native_loader::*; +pub mod instruction_processor; +pub mod instruction_recorder; +pub mod invoke_context; +pub mod log_collector; +pub mod native_loader; diff --git a/runtime/src/log_collector.rs b/program-runtime/src/log_collector.rs similarity index 100% rename from runtime/src/log_collector.rs rename to program-runtime/src/log_collector.rs diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index f037181339d687..88d222cee7d94a 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -9,7 +9,7 @@ use { log::*, solana_banks_client::start_client, solana_banks_server::banks_server::start_local_server, - solana_program_runtime::InstructionProcessor, + solana_program_runtime::instruction_processor::InstructionProcessor, solana_runtime::{ bank::{Bank, ExecuteTimings}, bank_forks::BankForks, @@ -33,7 +33,7 @@ use { message::Message, native_token::sol_to_lamports, poh_config::PohConfig, - process_instruction::{self, stable_log, InvokeContext, ProcessInstructionWithContext}, + process_instruction::{stable_log, InvokeContext, ProcessInstructionWithContext}, program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR}, pubkey::Pubkey, rent::Rent, @@ -215,7 +215,7 @@ fn get_sysvar( panic!("Exceeded compute budget"); } - match process_instruction::get_sysvar::(invoke_context, id) { + match solana_program_runtime::invoke_context::get_sysvar::(invoke_context, id) { Ok(sysvar_data) => unsafe { *(var_addr as *mut _ as *mut T) = sysvar_data; SUCCESS diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index ea127470cc5e14..a02199c17a2101 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2544,6 +2544,7 @@ dependencies = [ "solana-cli-output", "solana-logger 1.9.0", "solana-measure", + "solana-program-runtime", "solana-runtime", "solana-sdk", "solana-transaction-status", @@ -3208,6 +3209,7 @@ dependencies = [ name = "solana-program-runtime" version = "1.9.0" dependencies = [ + "bincode", "libc", "libloading", "log", @@ -3416,6 +3418,7 @@ dependencies = [ "solana-frozen-abi 1.9.0", "solana-frozen-abi-macro 1.9.0", "solana-metrics", + "solana-program-runtime", "solana-sdk", "solana-vote-program", "thiserror", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index 5ba2d0df8a087f..d22756581c0939 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -35,6 +35,7 @@ solana-logger = { path = "../../logger", version = "=1.9.0" } solana-measure = { path = "../../measure", version = "=1.9.0" } solana_rbpf = "=0.2.14" solana-runtime = { path = "../../runtime", version = "=1.9.0" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.9.0" } solana-sdk = { path = "../../sdk", version = "=1.9.0" } solana-transaction-status = { path = "../../transaction-status", version = "=1.9.0" } solana-account-decoder = { path = "../../account-decoder", version = "=1.9.0" } diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 68d3c25128d395..5826338e4a51d3 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -17,8 +17,8 @@ use solana_runtime::{ bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_program, - message_processor::with_mock_invoke_context, }; +use solana_program_runtime::invoke_context::with_mock_invoke_context; use solana_sdk::{ bpf_loader, client::SyncClient, diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 19f507d91f52b0..c7dc62ef43f4f8 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -30,8 +30,8 @@ use solana_runtime::{ load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority, upgrade_program, }, - message_processor::with_mock_invoke_context, }; +use solana_program_runtime::invoke_context::with_mock_invoke_context; use solana_sdk::{ account::{AccountSharedData, ReadableAccount}, account_utils::StateMut, diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 23b91f143ab9fd..ae844d90dd33fc 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -14,7 +14,7 @@ use crate::{ }; use log::{log_enabled, trace, Level::Trace}; use solana_measure::measure::Measure; -use solana_program_runtime::InstructionProcessor; +use solana_program_runtime::instruction_processor::InstructionProcessor; use solana_rbpf::{ aligned_memory::AlignedMemory, ebpf::HOST_ALIGN, @@ -1042,8 +1042,9 @@ impl Executor for BpfExecutor { mod tests { use super::*; use rand::Rng; + use solana_program_runtime::invoke_context::mock_process_instruction; use solana_rbpf::vm::SyscallRegistry; - use solana_runtime::{bank::Bank, bank_client::BankClient, message_processor}; + use solana_runtime::{bank::Bank, bank_client::BankClient}; use solana_sdk::{ account::{ create_account_shared_data_for_test as create_account_for_test, AccountSharedData, @@ -1082,7 +1083,7 @@ mod tests { instruction_data: &[u8], keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - message_processor::mock_process_instruction( + mock_process_instruction( loader_id, program_indices.to_vec(), instruction_data, @@ -1276,7 +1277,7 @@ mod tests { // Case: limited budget assert_eq!( Err(InstructionError::ProgramFailedToComplete), - message_processor::mock_process_instruction( + mock_process_instruction( &loader_id, vec![0], &[], diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index b4c812820e5320..a36af09d0b7f46 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -1,6 +1,6 @@ use crate::{alloc, BpfError}; use alloc::Alloc; -use solana_program_runtime::InstructionProcessor; +use solana_program_runtime::instruction_processor::InstructionProcessor; use solana_rbpf::{ aligned_memory::AlignedMemory, ebpf, @@ -31,7 +31,7 @@ use solana_sdk::{ message::Message, native_loader, precompiles::is_precompile, - process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger}, + process_instruction::{stable_log, ComputeMeter, InvokeContext, Logger}, program::MAX_RETURN_DATA, pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN}, rent::Rent, @@ -982,7 +982,7 @@ fn get_sysvar( .consume(invoke_context.get_compute_budget().sysvar_base_cost + size_of::() as u64)?; let var = translate_type_mut::(memory_mapping, var_addr, loader_id)?; - *var = process_instruction::get_sysvar::(*invoke_context, id) + *var = solana_program_runtime::invoke_context::get_sysvar::(*invoke_context, id) .map_err(SyscallError::InstructionError)?; Ok(SUCCESS) @@ -2427,13 +2427,13 @@ impl<'a> SyscallObject for SyscallLogData<'a> { #[cfg(test)] mod tests { use super::*; + use solana_program_runtime::{ + invoke_context::{ThisComputeMeter, ThisInvokeContext, ThisLogger}, + log_collector::LogCollector, + }; use solana_rbpf::{ ebpf::HOST_ALIGN, memory_region::MemoryRegion, user_error::UserError, vm::Config, }; - use solana_runtime::{ - log_collector::LogCollector, - message_processor::{ThisComputeMeter, ThisInvokeContext, ThisLogger}, - }; use solana_sdk::{ bpf_loader, feature_set::FeatureSet, fee_calculator::FeeCalculator, hash::hashv, }; diff --git a/programs/config/Cargo.toml b/programs/config/Cargo.toml index 93c8f56b0a1f7f..6861b708e9c951 100644 --- a/programs/config/Cargo.toml +++ b/programs/config/Cargo.toml @@ -18,7 +18,7 @@ solana-sdk = { path = "../../sdk", version = "=1.9.0" } [dev-dependencies] solana-logger = { path = "../../logger", version = "=1.9.0" } -solana-runtime = { path = "../../runtime", version = "=1.9.0" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.9.0" } [lib] crate-type = ["lib"] diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index bebd61a9ddaa7a..1cf2a753c293e7 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -138,7 +138,7 @@ mod tests { use crate::{config_instruction, get_config_data, id, ConfigKeys, ConfigState}; use bincode::serialized_size; use serde_derive::{Deserialize, Serialize}; - use solana_runtime::message_processor; + use solana_program_runtime::invoke_context::mock_process_instruction; use solana_sdk::{ account::AccountSharedData, pubkey::Pubkey, @@ -151,7 +151,7 @@ mod tests { instruction_data: &[u8], keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - message_processor::mock_process_instruction( + mock_process_instruction( &id(), Vec::new(), instruction_data, diff --git a/programs/stake/Cargo.toml b/programs/stake/Cargo.toml index de7d71bf57d02f..5aced68478198b 100644 --- a/programs/stake/Cargo.toml +++ b/programs/stake/Cargo.toml @@ -20,6 +20,7 @@ solana-frozen-abi = { path = "../../frozen-abi", version = "=1.9.0" } solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.9.0" } solana-metrics = { path = "../../metrics", version = "=1.9.0" } solana-sdk = { path = "../../sdk", version = "=1.9.0" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.9.0" } solana-vote-program = { path = "../vote", version = "=1.9.0" } solana-config-program = { path = "../config", version = "=1.9.0" } thiserror = "1.0" @@ -27,7 +28,6 @@ thiserror = "1.0" [dev-dependencies] proptest = "1.0" solana-logger = { path = "../../logger", version = "=1.9.0" } -solana-runtime = { path = "../../runtime", version = "=1.9.0" } [build-dependencies] rustc_version = "0.4" diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 63bc6da3685211..e87d829039d799 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -1,11 +1,12 @@ use { crate::{config, stake_state::StakeAccount}, log::*, + solana_program_runtime::invoke_context::get_sysvar, solana_sdk::{ feature_set, instruction::InstructionError, keyed_account::{from_keyed_account, get_signers, keyed_account_at_index}, - process_instruction::{get_sysvar, InvokeContext}, + process_instruction::InvokeContext, program_utils::limited_deserialize, stake::{ instruction::StakeInstruction, @@ -326,7 +327,7 @@ mod tests { use super::*; use crate::stake_state::{Meta, StakeState}; use bincode::serialize; - use solana_runtime::message_processor::{ + use solana_program_runtime::invoke_context::{ mock_process_instruction, prepare_mock_invoke_context, ThisInvokeContext, }; use solana_sdk::{ diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 4a95869ef49467..e6be6fce59a3a7 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -1373,7 +1373,7 @@ fn do_create_account( mod tests { use super::*; use proptest::prelude::*; - use solana_runtime::message_processor::ThisInvokeContext; + use solana_program_runtime::invoke_context::ThisInvokeContext; use solana_sdk::{ account::{AccountSharedData, WritableAccount}, clock::UnixTimestamp, diff --git a/programs/vote/Cargo.toml b/programs/vote/Cargo.toml index 6a8c76c6c05ac5..b8e00af3aa1a66 100644 --- a/programs/vote/Cargo.toml +++ b/programs/vote/Cargo.toml @@ -24,7 +24,7 @@ solana-sdk = { path = "../../sdk", version = "=1.9.0" } thiserror = "1.0" [dev-dependencies] -solana-runtime = { path = "../../runtime", version = "=1.9.0" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.9.0" } [build-dependencies] rustc_version = "0.4" diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index e3078be7bc1d91..7f54f3a1d41da2 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -405,7 +405,7 @@ pub fn process_instruction( mod tests { use super::*; use bincode::serialize; - use solana_runtime::message_processor; + use solana_program_runtime::invoke_context::mock_process_instruction; use solana_sdk::{ account::{self, Account, AccountSharedData}, rent::Rent, @@ -421,7 +421,7 @@ mod tests { instruction_data: &[u8], keyed_accounts: &[(bool, bool, Pubkey, Rc>)], ) -> Result<(), InstructionError> { - message_processor::mock_process_instruction( + mock_process_instruction( &id(), Vec::new(), instruction_data, @@ -464,7 +464,7 @@ mod tests { .zip(accounts.into_iter()) .map(|(meta, account)| (meta.is_signer, meta.is_writable, meta.pubkey, account)) .collect(); - message_processor::mock_process_instruction( + solana_program_runtime::invoke_context::mock_process_instruction( &id(), Vec::new(), &instruction.data, diff --git a/rbpf-cli/Cargo.toml b/rbpf-cli/Cargo.toml index 767ca7a5a4af18..2097a8a590a58a 100644 --- a/rbpf-cli/Cargo.toml +++ b/rbpf-cli/Cargo.toml @@ -14,7 +14,7 @@ serde = "1.0.130" serde_json = "1.0.68" solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.9.0" } solana-logger = { path = "../logger", version = "=1.9.0" } +solana-program-runtime = { path = "../program-runtime", version = "=1.9.0" } solana-sdk = { path = "../sdk", version = "=1.9.0" } solana_rbpf = "=0.2.14" -solana-runtime = { path = "../runtime", version = "=1.9.0" } time = "0.3.4" diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index 33ee445557b9b1..b693931dc2c1bf 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -5,13 +5,13 @@ use solana_bpf_loader_program::{ create_vm, serialization::serialize_parameters, syscalls::register_syscalls, BpfError, ThisInstructionMeter, }; +use solana_program_runtime::invoke_context::{prepare_mock_invoke_context, ThisInvokeContext}; use solana_rbpf::{ assembler::assemble, static_analysis::Analysis, verifier::check, vm::{Config, DynamicAnalysis, Executable}, }; -use solana_runtime::message_processor::{prepare_mock_invoke_context, ThisInvokeContext}; use solana_sdk::{ account::AccountSharedData, bpf_loader, process_instruction::InvokeContext, pubkey::Pubkey, }; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 1ba9d5eef6785b..51ec054831bd45 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -47,8 +47,6 @@ use crate::{ cost_tracker::CostTracker, epoch_stakes::{EpochStakes, NodeVoteAccounts}, inline_spl_token_v2_0, - instruction_recorder::InstructionRecorder, - log_collector::LogCollector, message_processor::MessageProcessor, rent_collector::RentCollector, stake_weighted_timestamp::{ @@ -71,7 +69,11 @@ use rayon::{ }; use solana_measure::measure::Measure; use solana_metrics::{inc_new_counter_debug, inc_new_counter_info}; -use solana_program_runtime::{ExecuteDetailsTimings, Executors, InstructionProcessor}; +use solana_program_runtime::{ + instruction_processor::{ExecuteDetailsTimings, Executors, InstructionProcessor}, + instruction_recorder::InstructionRecorder, + log_collector::LogCollector, +}; #[allow(deprecated)] use solana_sdk::recent_blockhashes_account; use solana_sdk::{ @@ -3877,7 +3879,7 @@ impl Bank { legacy_message, &loaded_transaction.program_indices, &account_refcells, - &self.rent_collector, + self.rent_collector.rent, log_collector.clone(), executors.clone(), instruction_recorders.as_deref(), diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a6ab24c48aa62a..65e1cfca85ec72 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -32,9 +32,7 @@ pub mod genesis_utils; pub mod hardened_unpack; pub mod in_mem_accounts_index; pub mod inline_spl_token_v2_0; -pub mod instruction_recorder; pub mod loader_utils; -pub mod log_collector; pub mod message_processor; pub mod neon_evm_program; pub mod non_circulating_supply; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 9a375ca349ae70..3422bbc79972a5 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -1,29 +1,22 @@ -use crate::{ - instruction_recorder::InstructionRecorder, log_collector::LogCollector, - rent_collector::RentCollector, -}; -use log::*; use serde::{Deserialize, Serialize}; use solana_measure::measure::Measure; -use solana_program_runtime::{ExecuteDetailsTimings, Executors, InstructionProcessor, PreAccount}; +use solana_program_runtime::{ + instruction_processor::{ExecuteDetailsTimings, Executors, InstructionProcessor}, + instruction_recorder::InstructionRecorder, + invoke_context::ThisInvokeContext, + log_collector::LogCollector, +}; use solana_sdk::{ - account::{AccountSharedData, ReadableAccount, WritableAccount}, + account::{AccountSharedData, WritableAccount}, compute_budget::ComputeBudget, feature_set::{ - demote_program_write_locks, do_support_realloc, neon_evm_compute_budget, - prevent_calling_precompiles_as_programs, remove_native_loader, requestable_heap_size, + neon_evm_compute_budget, prevent_calling_precompiles_as_programs, requestable_heap_size, tx_wide_compute_cap, FeatureSet, }, hash::Hash, - ic_logger_msg, - instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, - keyed_account::{create_keyed_accounts_unified, KeyedAccount}, message::Message, precompiles::is_precompile, - process_instruction::{ - ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger, - ProcessInstructionWithContext, - }, + process_instruction::{ComputeMeter, InvokeContext}, pubkey::Pubkey, rent::Rent, sysvar::instructions, @@ -31,575 +24,6 @@ use solana_sdk::{ }; use std::{cell::RefCell, rc::Rc, sync::Arc}; -pub struct ThisComputeMeter { - remaining: u64, -} -impl ComputeMeter for ThisComputeMeter { - fn consume(&mut self, amount: u64) -> Result<(), InstructionError> { - let exceeded = self.remaining < amount; - self.remaining = self.remaining.saturating_sub(amount); - if exceeded { - return Err(InstructionError::ComputationalBudgetExceeded); - } - Ok(()) - } - fn get_remaining(&self) -> u64 { - self.remaining - } -} -impl ThisComputeMeter { - pub fn new_ref(remaining: u64) -> Rc> { - Rc::new(RefCell::new(Self { remaining })) - } -} - -pub struct ThisLogger { - log_collector: Option>, -} -impl Logger for ThisLogger { - fn log_enabled(&self) -> bool { - log_enabled!(log::Level::Info) || self.log_collector.is_some() - } - fn log(&self, message: &str) { - debug!("{}", message); - if let Some(log_collector) = &self.log_collector { - log_collector.log(message); - } - } -} -impl ThisLogger { - pub fn new_ref(log_collector: Option>) -> Rc> { - Rc::new(RefCell::new(Self { log_collector })) - } -} - -pub struct ThisInvokeContext<'a> { - instruction_index: usize, - invoke_stack: Vec>, - rent: Rent, - pre_accounts: Vec, - accounts: &'a [(Pubkey, Rc>)], - programs: &'a [(Pubkey, ProcessInstructionWithContext)], - sysvars: &'a [(Pubkey, Vec)], - logger: Rc>, - compute_budget: ComputeBudget, - compute_meter: Rc>, - executors: Rc>, - instruction_recorders: Option<&'a [InstructionRecorder]>, - feature_set: Arc, - pub timings: ExecuteDetailsTimings, - blockhash: Hash, - lamports_per_signature: u64, - return_data: (Pubkey, Vec), -} -impl<'a> ThisInvokeContext<'a> { - #[allow(clippy::too_many_arguments)] - pub fn new( - rent: Rent, - accounts: &'a [(Pubkey, Rc>)], - programs: &'a [(Pubkey, ProcessInstructionWithContext)], - sysvars: &'a [(Pubkey, Vec)], - log_collector: Option>, - compute_budget: ComputeBudget, - compute_meter: Rc>, - executors: Rc>, - instruction_recorders: Option<&'a [InstructionRecorder]>, - feature_set: Arc, - blockhash: Hash, - lamports_per_signature: u64, - ) -> Self { - Self { - instruction_index: 0, - invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth), - rent, - pre_accounts: Vec::new(), - accounts, - programs, - sysvars, - logger: ThisLogger::new_ref(log_collector), - compute_budget, - compute_meter, - executors, - instruction_recorders, - feature_set, - timings: ExecuteDetailsTimings::default(), - blockhash, - lamports_per_signature, - return_data: (Pubkey::default(), Vec::new()), - } - } - - pub fn new_mock_with_sysvars_and_features( - accounts: &'a [(Pubkey, Rc>)], - programs: &'a [(Pubkey, ProcessInstructionWithContext)], - sysvars: &'a [(Pubkey, Vec)], - feature_set: Arc, - ) -> Self { - Self::new( - Rent::default(), - accounts, - programs, - sysvars, - None, - ComputeBudget::default(), - ThisComputeMeter::new_ref(std::i64::MAX as u64), - Rc::new(RefCell::new(Executors::default())), - None, - feature_set, - Hash::default(), - 0, - ) - } - - pub fn new_mock( - accounts: &'a [(Pubkey, Rc>)], - programs: &'a [(Pubkey, ProcessInstructionWithContext)], - ) -> Self { - Self::new_mock_with_sysvars_and_features( - accounts, - programs, - &[], - Arc::new(FeatureSet::all_enabled()), - ) - } -} -impl<'a> InvokeContext for ThisInvokeContext<'a> { - fn push( - &mut self, - message: &Message, - instruction: &CompiledInstruction, - program_indices: &[usize], - account_indices: Option<&[usize]>, - ) -> Result<(), InstructionError> { - if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { - return Err(InstructionError::CallDepth); - } - - if let Some(index_of_program_id) = program_indices.last() { - let program_id = &self.accounts[*index_of_program_id].0; - let contains = self - .invoke_stack - .iter() - .any(|frame| frame.program_id() == Some(program_id)); - let is_last = if let Some(last_frame) = self.invoke_stack.last() { - last_frame.program_id() == Some(program_id) - } else { - false - }; - if contains && !is_last { - // Reentrancy not allowed unless caller is calling itself - return Err(InstructionError::ReentrancyNotAllowed); - } - } - - if self.invoke_stack.is_empty() { - if !self.feature_set.is_active(&tx_wide_compute_cap::id()) { - self.compute_meter = ThisComputeMeter::new_ref(self.compute_budget.max_units); - } - - self.pre_accounts = Vec::with_capacity(instruction.accounts.len()); - let mut work = |_unique_index: usize, account_index: usize| { - if account_index < self.accounts.len() { - let account = self.accounts[account_index].1.borrow(); - self.pre_accounts - .push(PreAccount::new(&self.accounts[account_index].0, &account)); - return Ok(()); - } - Err(InstructionError::MissingAccount) - }; - instruction.visit_each_account(&mut work)?; - } - - // Create the KeyedAccounts that will be passed to the program - let demote_program_write_locks = self - .feature_set - .is_active(&demote_program_write_locks::id()); - let keyed_accounts = program_indices - .iter() - .map(|account_index| { - ( - false, - false, - &self.accounts[*account_index].0, - &self.accounts[*account_index].1 as &RefCell, - ) - }) - .chain(instruction.accounts.iter().map(|index_in_instruction| { - let index_in_instruction = *index_in_instruction as usize; - let account_index = if let Some(account_indices) = account_indices { - account_indices[index_in_instruction] - } else { - index_in_instruction - }; - ( - message.is_signer(index_in_instruction), - message.is_writable(index_in_instruction, demote_program_write_locks), - &self.accounts[account_index].0, - &self.accounts[account_index].1 as &RefCell, - ) - })) - .collect::>(); - - self.invoke_stack.push(InvokeContextStackFrame::new( - program_indices.len(), - create_keyed_accounts_unified(keyed_accounts.as_slice()), - )); - Ok(()) - } - fn pop(&mut self) { - self.invoke_stack.pop(); - } - fn invoke_depth(&self) -> usize { - self.invoke_stack.len() - } - fn verify( - &mut self, - message: &Message, - instruction: &CompiledInstruction, - program_indices: &[usize], - ) -> Result<(), InstructionError> { - let program_id = instruction.program_id(&message.account_keys); - let demote_program_write_locks = self.is_feature_active(&demote_program_write_locks::id()); - let do_support_realloc = self.is_feature_active(&do_support_realloc::id()); - - // Verify all executable accounts have zero outstanding refs - for account_index in program_indices.iter() { - self.accounts[*account_index] - .1 - .try_borrow_mut() - .map_err(|_| InstructionError::AccountBorrowOutstanding)?; - } - - // Verify the per-account instruction results - let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); - let mut work = |unique_index: usize, account_index: usize| { - { - // Verify account has no outstanding references - let _ = self.accounts[account_index] - .1 - .try_borrow_mut() - .map_err(|_| InstructionError::AccountBorrowOutstanding)?; - } - let account = self.accounts[account_index].1.borrow(); - self.pre_accounts[unique_index] - .verify( - program_id, - message.is_writable(account_index, demote_program_write_locks), - &self.rent, - &account, - &mut self.timings, - true, - do_support_realloc, - ) - .map_err(|err| { - ic_logger_msg!( - self.logger, - "failed to verify account {}: {}", - self.pre_accounts[unique_index].key(), - err - ); - err - })?; - pre_sum += u128::from(self.pre_accounts[unique_index].lamports()); - post_sum += u128::from(account.lamports()); - Ok(()) - }; - instruction.visit_each_account(&mut work)?; - - // Verify that the total sum of all the lamports did not change - if pre_sum != post_sum { - return Err(InstructionError::UnbalancedInstruction); - } - Ok(()) - } - fn verify_and_update( - &mut self, - instruction: &CompiledInstruction, - account_indices: &[usize], - write_privileges: &[bool], - ) -> Result<(), InstructionError> { - let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); - let program_id = self - .invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth)?; - let rent = &self.rent; - let logger = &self.logger; - let accounts = &self.accounts; - let pre_accounts = &mut self.pre_accounts; - let timings = &mut self.timings; - - // Verify the per-account instruction results - let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); - let mut work = |_unique_index: usize, index_in_instruction: usize| { - if index_in_instruction < write_privileges.len() - && index_in_instruction < account_indices.len() - { - let account_index = account_indices[index_in_instruction]; - let (key, account) = &accounts[account_index]; - let is_writable = write_privileges[index_in_instruction]; - // Find the matching PreAccount - for pre_account in pre_accounts.iter_mut() { - if key == pre_account.key() { - { - // Verify account has no outstanding references - let _ = account - .try_borrow_mut() - .map_err(|_| InstructionError::AccountBorrowOutstanding)?; - } - let account = account.borrow(); - pre_account - .verify( - program_id, - is_writable, - rent, - &account, - timings, - false, - do_support_realloc, - ) - .map_err(|err| { - ic_logger_msg!(logger, "failed to verify account {}: {}", key, err); - err - })?; - pre_sum += u128::from(pre_account.lamports()); - post_sum += u128::from(account.lamports()); - if is_writable && !pre_account.executable() { - pre_account.update(&account); - } - return Ok(()); - } - } - } - Err(InstructionError::MissingAccount) - }; - instruction.visit_each_account(&mut work)?; - - // Verify that the total sum of all the lamports did not change - if pre_sum != post_sum { - return Err(InstructionError::UnbalancedInstruction); - } - Ok(()) - } - fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - self.invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth) - } - fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { - if !self.is_feature_active(&remove_native_loader::id()) { - let stack_frame = &mut self - .invoke_stack - .last_mut() - .ok_or(InstructionError::CallDepth)?; - stack_frame.keyed_accounts_range.start = - stack_frame.keyed_accounts_range.start.saturating_add(1); - } - Ok(()) - } - fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { - self.invoke_stack - .last() - .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()]) - .ok_or(InstructionError::CallDepth) - } - fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] { - self.programs - } - fn get_logger(&self) -> Rc> { - self.logger.clone() - } - fn get_compute_meter(&self) -> Rc> { - self.compute_meter.clone() - } - fn add_executor(&self, pubkey: &Pubkey, executor: Arc) { - self.executors.borrow_mut().insert(*pubkey, executor); - } - fn get_executor(&self, pubkey: &Pubkey) -> Option> { - self.executors.borrow().get(pubkey) - } - fn set_instruction_index(&mut self, instruction_index: usize) { - self.instruction_index = instruction_index; - } - fn record_instruction(&self, instruction: &Instruction) { - if let Some(instruction_recorders) = &self.instruction_recorders { - instruction_recorders[self.instruction_index].record_instruction(instruction.clone()); - } - } - fn is_feature_active(&self, feature_id: &Pubkey) -> bool { - self.feature_set.is_active(feature_id) - } - fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { - for (index, (key, account)) in self.accounts.iter().enumerate().rev() { - if key == pubkey { - return Some((index, account.clone())); - } - } - None - } - fn update_timing( - &mut self, - serialize_us: u64, - create_vm_us: u64, - execute_us: u64, - deserialize_us: u64, - ) { - self.timings.serialize_us += serialize_us; - self.timings.create_vm_us += create_vm_us; - self.timings.execute_us += execute_us; - self.timings.deserialize_us += deserialize_us; - } - fn get_sysvars(&self) -> &[(Pubkey, Vec)] { - self.sysvars - } - fn get_compute_budget(&self) -> &ComputeBudget { - &self.compute_budget - } - fn set_blockhash(&mut self, hash: Hash) { - self.blockhash = hash; - } - fn get_blockhash(&self) -> &Hash { - &self.blockhash - } - fn set_lamports_per_signature(&mut self, lamports_per_signature: u64) { - self.lamports_per_signature = lamports_per_signature; - } - fn get_lamports_per_signature(&self) -> u64 { - self.lamports_per_signature - } - fn set_return_data(&mut self, data: Vec) -> Result<(), InstructionError> { - self.return_data = (*self.get_caller()?, data); - Ok(()) - } - fn get_return_data(&self) -> (Pubkey, &[u8]) { - (self.return_data.0, &self.return_data.1) - } -} - -pub struct MockInvokeContextPreparation { - pub accounts: Vec<(Pubkey, Rc>)>, - pub message: Message, - pub account_indices: Vec, -} - -pub fn prepare_mock_invoke_context( - program_indices: &[usize], - instruction_data: &[u8], - keyed_accounts: &[(bool, bool, Pubkey, Rc>)], -) -> MockInvokeContextPreparation { - #[allow(clippy::type_complexity)] - let (accounts, mut metas): ( - Vec<(Pubkey, Rc>)>, - Vec, - ) = keyed_accounts - .iter() - .map(|(is_signer, is_writable, pubkey, account)| { - ( - (*pubkey, account.clone()), - AccountMeta { - pubkey: *pubkey, - is_signer: *is_signer, - is_writable: *is_writable, - }, - ) - }) - .unzip(); - let program_id = if let Some(program_index) = program_indices.last() { - accounts[*program_index].0 - } else { - Pubkey::default() - }; - for program_index in program_indices.iter().rev() { - metas.remove(*program_index); - } - let message = Message::new( - &[Instruction::new_with_bytes( - program_id, - instruction_data, - metas, - )], - None, - ); - let account_indices: Vec = message - .account_keys - .iter() - .map(|search_key| { - accounts - .iter() - .position(|(key, _account)| key == search_key) - .unwrap_or(accounts.len()) - }) - .collect(); - MockInvokeContextPreparation { - accounts, - message, - account_indices, - } -} - -pub fn with_mock_invoke_context R>( - loader_id: Pubkey, - account_size: usize, - mut callback: F, -) -> R { - let program_indices = vec![0, 1]; - let keyed_accounts = [ - ( - false, - false, - loader_id, - AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()), - ), - ( - false, - false, - Pubkey::new_unique(), - AccountSharedData::new_ref(1, 0, &loader_id), - ), - ( - false, - false, - Pubkey::new_unique(), - AccountSharedData::new_ref(2, account_size, &Pubkey::new_unique()), - ), - ]; - let preparation = prepare_mock_invoke_context(&program_indices, &[], &keyed_accounts); - let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); - invoke_context - .push( - &preparation.message, - &preparation.message.instructions[0], - &program_indices, - Some(&preparation.account_indices), - ) - .unwrap(); - callback(&mut invoke_context) -} - -pub fn mock_process_instruction( - loader_id: &Pubkey, - mut program_indices: Vec, - instruction_data: &[u8], - keyed_accounts: &[(bool, bool, Pubkey, Rc>)], - process_instruction: ProcessInstructionWithContext, -) -> Result<(), InstructionError> { - let mut preparation = - prepare_mock_invoke_context(&program_indices, instruction_data, keyed_accounts); - let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); - program_indices.insert(0, preparation.accounts.len()); - preparation.accounts.push((*loader_id, processor_account)); - let mut invoke_context = ThisInvokeContext::new_mock(&preparation.accounts, &[]); - invoke_context.push( - &preparation.message, - &preparation.message.instructions[0], - &program_indices, - Some(&preparation.account_indices), - )?; - process_instruction(1, instruction_data, &mut invoke_context) -} - #[derive(Debug, Default, Clone, Deserialize, Serialize)] pub struct MessageProcessor {} @@ -624,7 +48,7 @@ impl MessageProcessor { message: &Message, program_indices: &[Vec], accounts: &[(Pubkey, Rc>)], - rent_collector: &RentCollector, + rent: Rent, log_collector: Option>, executors: Rc>, instruction_recorders: Option<&[InstructionRecorder]>, @@ -637,7 +61,7 @@ impl MessageProcessor { lamports_per_signature: u64, ) -> Result<(), TransactionError> { let mut invoke_context = ThisInvokeContext::new( - rent_collector.rent, + rent, accounts, instruction_processor.programs(), sysvars, @@ -729,7 +153,10 @@ impl MessageProcessor { #[cfg(test)] mod tests { use super::*; + use crate::rent_collector::RentCollector; + use solana_program_runtime::invoke_context::ThisComputeMeter; use solana_sdk::{ + account::ReadableAccount, instruction::{AccountMeta, Instruction, InstructionError}, keyed_account::keyed_account_at_index, message::Message, @@ -747,185 +174,6 @@ mod tests { ModifyReadonly, } - fn mock_process_instruction( - first_instruction_account: usize, - data: &[u8], - invoke_context: &mut dyn InvokeContext, - ) -> Result<(), InstructionError> { - let program_id = invoke_context.get_caller()?; - let keyed_accounts = invoke_context.get_keyed_accounts()?; - assert_eq!( - *program_id, - keyed_account_at_index(keyed_accounts, first_instruction_account)?.owner()? - ); - assert_ne!( - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.owner()?, - *keyed_account_at_index(keyed_accounts, first_instruction_account)?.unsigned_key() - ); - - if let Ok(instruction) = bincode::deserialize(data) { - match instruction { - MockInstruction::NoopSuccess => (), - MockInstruction::NoopFail => return Err(InstructionError::GenericError), - MockInstruction::ModifyOwned => { - keyed_account_at_index(keyed_accounts, first_instruction_account)? - .try_account_ref_mut()? - .data_as_mut_slice()[0] = 1 - } - MockInstruction::ModifyNotOwned => { - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)? - .try_account_ref_mut()? - .data_as_mut_slice()[0] = 1 - } - MockInstruction::ModifyReadonly => { - keyed_account_at_index(keyed_accounts, first_instruction_account + 2)? - .try_account_ref_mut()? - .data_as_mut_slice()[0] = 1 - } - } - } else { - return Err(InstructionError::InvalidInstructionData); - } - Ok(()) - } - - #[test] - fn test_invoke_context() { - const MAX_DEPTH: usize = 10; - let mut invoke_stack = vec![]; - let mut accounts = vec![]; - let mut metas = vec![]; - for i in 0..MAX_DEPTH { - invoke_stack.push(solana_sdk::pubkey::new_rand()); - accounts.push(( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(AccountSharedData::new( - i as u64, - 1, - &invoke_stack[i], - ))), - )); - metas.push(AccountMeta::new(accounts[i].0, false)); - } - for program_id in invoke_stack.iter() { - accounts.push(( - *program_id, - Rc::new(RefCell::new(AccountSharedData::new( - 1, - 1, - &solana_sdk::pubkey::Pubkey::default(), - ))), - )); - metas.push(AccountMeta::new(*program_id, false)); - } - let account_indices = (0..accounts.len()).collect::>(); - - let message = Message::new( - &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)], - None, - ); - let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]); - - // Check call depth increases and has a limit - let mut depth_reached = 0; - for _ in 0..invoke_stack.len() { - if Err(InstructionError::CallDepth) - == invoke_context.push( - &message, - &message.instructions[0], - &[MAX_DEPTH + depth_reached], - None, - ) - { - break; - } - depth_reached += 1; - } - assert_ne!(depth_reached, 0); - assert!(depth_reached < MAX_DEPTH); - - // Mock each invocation - for owned_index in (1..depth_reached).rev() { - let not_owned_index = owned_index - 1; - let metas = vec![ - AccountMeta::new(accounts[not_owned_index].0, false), - AccountMeta::new(accounts[owned_index].0, false), - ]; - let message = Message::new( - &[Instruction::new_with_bytes( - invoke_stack[owned_index], - &[0], - metas, - )], - None, - ); - let write_privileges: Vec = (0..message.account_keys.len()) - .map(|i| message.is_writable(i, /*demote_program_write_locks=*/ true)) - .collect(); - - // modify account owned by the program - accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] = - (MAX_DEPTH + owned_index) as u8; - invoke_context - .verify_and_update( - &message.instructions[0], - &account_indices[not_owned_index..owned_index + 1], - &write_privileges, - ) - .unwrap(); - assert_eq!( - invoke_context.pre_accounts[owned_index].data()[0], - (MAX_DEPTH + owned_index) as u8 - ); - - // modify account not owned by the program - let data = accounts[not_owned_index].1.borrow_mut().data()[0]; - accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = - (MAX_DEPTH + not_owned_index) as u8; - assert_eq!( - invoke_context.verify_and_update( - &message.instructions[0], - &account_indices[not_owned_index..owned_index + 1], - &write_privileges, - ), - Err(InstructionError::ExternalAccountDataModified) - ); - assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data); - accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = data; - - invoke_context.pop(); - } - } - - #[test] - fn test_invoke_context_verify() { - let accounts = vec![( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(AccountSharedData::default())), - )]; - let message = Message::new( - &[Instruction::new_with_bincode( - accounts[0].0, - &MockInstruction::NoopSuccess, - vec![AccountMeta::new_readonly(accounts[0].0, false)], - )], - None, - ); - let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]); - invoke_context - .push(&message, &message.instructions[0], &[0], None) - .unwrap(); - assert!(invoke_context - .verify(&message, &message.instructions[0], &[0]) - .is_ok()); - - let mut _borrowed = accounts[0].1.borrow(); - assert_eq!( - invoke_context.verify(&message, &message.instructions[0], &[0]), - Err(InstructionError::AccountBorrowOutstanding) - ); - } - #[test] fn test_process_message_readonly_handling() { #[derive(Serialize, Deserialize)] @@ -1010,7 +258,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors.clone(), None, @@ -1040,7 +288,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors.clone(), None, @@ -1074,7 +322,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors, None, @@ -1217,7 +465,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors.clone(), None, @@ -1251,7 +499,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors.clone(), None, @@ -1282,7 +530,7 @@ mod tests { &message, &program_indices, &accounts, - &rent_collector, + rent_collector.rent, None, executors, None, @@ -1300,241 +548,6 @@ mod tests { assert_eq!(accounts[0].1.borrow().data(), &vec![42]); } - #[test] - fn test_process_cross_program() { - let caller_program_id = solana_sdk::pubkey::new_rand(); - let callee_program_id = solana_sdk::pubkey::new_rand(); - - let owned_account = AccountSharedData::new(42, 1, &callee_program_id); - let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); - let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand()); - let loader_account = AccountSharedData::new(0, 0, &native_loader::id()); - let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); - program_account.set_executable(true); - - let accounts = vec![ - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(owned_account)), - ), - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(not_owned_account)), - ), - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(readonly_account)), - ), - (caller_program_id, Rc::new(RefCell::new(loader_account))), - (callee_program_id, Rc::new(RefCell::new(program_account))), - ]; - let account_indices = [0, 1, 2]; - let program_indices = [3, 4]; - - let programs: Vec<(_, ProcessInstructionWithContext)> = - vec![(callee_program_id, mock_process_instruction)]; - let metas = vec![ - AccountMeta::new(accounts[0].0, false), - AccountMeta::new(accounts[1].0, false), - AccountMeta::new_readonly(accounts[2].0, false), - ]; - - let caller_instruction = - CompiledInstruction::new(program_indices[0] as u8, &(), vec![0, 1, 2, 3, 4]); - let callee_instruction = Instruction::new_with_bincode( - callee_program_id, - &MockInstruction::NoopSuccess, - metas.clone(), - ); - let message = Message::new(&[callee_instruction], None); - - let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice()); - invoke_context - .push(&message, &caller_instruction, &program_indices[..1], None) - .unwrap(); - - // not owned account modified by the caller (before the invoke) - let demote_program_write_locks = - invoke_context.is_feature_active(&demote_program_write_locks::id()); - let caller_write_privileges = message - .account_keys - .iter() - .enumerate() - .map(|(i, _)| message.is_writable(i, demote_program_write_locks)) - .collect::>(); - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1; - assert_eq!( - InstructionProcessor::process_cross_program_instruction( - &message, - &program_indices[1..], - &account_indices, - &caller_write_privileges, - &mut invoke_context, - ), - Err(InstructionError::ExternalAccountDataModified) - ); - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0; - - // readonly account modified by the invoker - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; - assert_eq!( - InstructionProcessor::process_cross_program_instruction( - &message, - &program_indices[1..], - &account_indices, - &caller_write_privileges, - &mut invoke_context, - ), - Err(InstructionError::ReadonlyDataModified) - ); - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; - - invoke_context.pop(); - - let cases = vec![ - (MockInstruction::NoopSuccess, Ok(())), - ( - MockInstruction::NoopFail, - Err(InstructionError::GenericError), - ), - (MockInstruction::ModifyOwned, Ok(())), - ( - MockInstruction::ModifyNotOwned, - Err(InstructionError::ExternalAccountDataModified), - ), - ]; - for case in cases { - let callee_instruction = - Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); - let message = Message::new(&[callee_instruction], None); - invoke_context - .push(&message, &caller_instruction, &program_indices[..1], None) - .unwrap(); - let caller_write_privileges = message - .account_keys - .iter() - .enumerate() - .map(|(i, _)| message.is_writable(i, demote_program_write_locks)) - .collect::>(); - assert_eq!( - InstructionProcessor::process_cross_program_instruction( - &message, - &program_indices[1..], - &account_indices, - &caller_write_privileges, - &mut invoke_context, - ), - case.1 - ); - invoke_context.pop(); - } - } - - #[test] - fn test_native_invoke() { - let caller_program_id = solana_sdk::pubkey::new_rand(); - let callee_program_id = solana_sdk::pubkey::new_rand(); - - let owned_account = AccountSharedData::new(42, 1, &callee_program_id); - let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); - let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand()); - let loader_account = AccountSharedData::new(0, 0, &native_loader::id()); - let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); - program_account.set_executable(true); - - let accounts = vec![ - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(owned_account)), - ), - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(not_owned_account)), - ), - ( - solana_sdk::pubkey::new_rand(), - Rc::new(RefCell::new(readonly_account)), - ), - (caller_program_id, Rc::new(RefCell::new(loader_account))), - (callee_program_id, Rc::new(RefCell::new(program_account))), - ]; - let program_indices = [3]; - let programs: Vec<(_, ProcessInstructionWithContext)> = - vec![(callee_program_id, mock_process_instruction)]; - let metas = vec![ - AccountMeta::new(accounts[0].0, false), - AccountMeta::new(accounts[1].0, false), - AccountMeta::new_readonly(accounts[2].0, false), - ]; - - let caller_instruction = - CompiledInstruction::new(program_indices[0] as u8, &(), vec![0, 1, 2, 3, 4]); - let callee_instruction = Instruction::new_with_bincode( - callee_program_id, - &MockInstruction::NoopSuccess, - metas.clone(), - ); - let message = Message::new(&[callee_instruction.clone()], None); - - let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice()); - invoke_context - .push(&message, &caller_instruction, &program_indices, None) - .unwrap(); - - // not owned account modified by the invoker - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1; - assert_eq!( - InstructionProcessor::native_invoke( - &mut invoke_context, - callee_instruction.clone(), - &[] - ), - Err(InstructionError::ExternalAccountDataModified) - ); - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0; - - // readonly account modified by the invoker - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; - assert_eq!( - InstructionProcessor::native_invoke(&mut invoke_context, callee_instruction, &[]), - Err(InstructionError::ReadonlyDataModified) - ); - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; - - invoke_context.pop(); - - // Other test cases - let cases = vec![ - (MockInstruction::NoopSuccess, Ok(())), - ( - MockInstruction::NoopFail, - Err(InstructionError::GenericError), - ), - (MockInstruction::ModifyOwned, Ok(())), - ( - MockInstruction::ModifyNotOwned, - Err(InstructionError::ExternalAccountDataModified), - ), - ( - MockInstruction::ModifyReadonly, - Err(InstructionError::ReadonlyDataModified), - ), - ]; - for case in cases { - let callee_instruction = - Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); - let message = Message::new(&[callee_instruction.clone()], None); - invoke_context - .push(&message, &caller_instruction, &program_indices, None) - .unwrap(); - assert_eq!( - InstructionProcessor::native_invoke(&mut invoke_context, callee_instruction, &[]), - case.1 - ); - invoke_context.pop(); - } - } - #[test] fn test_precompile() { let mock_program_id = Pubkey::new_unique(); @@ -1573,7 +586,7 @@ mod tests { &message, &[vec![0], vec![1]], &accounts, - &RentCollector::default(), + RentCollector::default().rent, None, Rc::new(RefCell::new(Executors::default())), None, diff --git a/runtime/src/nonce_keyed_account.rs b/runtime/src/nonce_keyed_account.rs index 95301b8d8524f5..fa29ade3fe0895 100644 --- a/runtime/src/nonce_keyed_account.rs +++ b/runtime/src/nonce_keyed_account.rs @@ -254,7 +254,7 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { #[cfg(test)] mod test { use super::*; - use crate::message_processor::ThisInvokeContext; + use solana_program_runtime::invoke_context::ThisInvokeContext; use solana_sdk::{ account::ReadableAccount, account_utils::State as AccountUtilsState, diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index 4860360970bcd1..844f933bf05c20 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -24,7 +24,7 @@ use { rayon::prelude::*, serde::{de::DeserializeOwned, Deserialize, Serialize}, solana_measure::measure::Measure, - solana_program_runtime::InstructionProcessor, + solana_program_runtime::instruction_processor::InstructionProcessor, solana_sdk::{ clock::{Epoch, Slot, UnixTimestamp}, epoch_schedule::EpochSchedule, diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index a6835ab650261c..75e469722842d4 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -479,12 +479,9 @@ pub fn get_system_account_kind(account: &AccountSharedData) -> Option Result<(), InstructionError>; -pub struct InvokeContextStackFrame<'a> { - pub number_of_program_accounts: usize, - pub keyed_accounts: Vec>, - pub keyed_accounts_range: std::ops::Range, -} - -impl<'a> InvokeContextStackFrame<'a> { - pub fn new(number_of_program_accounts: usize, keyed_accounts: Vec>) -> Self { - let keyed_accounts_range = std::ops::Range { - start: 0, - end: keyed_accounts.len(), - }; - Self { - number_of_program_accounts, - keyed_accounts, - keyed_accounts_range, - } - } - - pub fn program_id(&self) -> Option<&Pubkey> { - self.keyed_accounts - .get(self.number_of_program_accounts.saturating_sub(1)) - .map(|keyed_account| keyed_account.unsigned_key()) - } -} - /// Invocation context passed to loaders pub trait InvokeContext { /// Push a stack frame onto the invocation stack @@ -167,26 +140,6 @@ macro_rules! ic_msg { }; } -pub fn get_sysvar( - invoke_context: &dyn InvokeContext, - id: &Pubkey, -) -> Result { - invoke_context - .get_sysvars() - .iter() - .find_map(|(key, data)| { - if id == key { - bincode::deserialize(data).ok() - } else { - None - } - }) - .ok_or_else(|| { - ic_msg!(invoke_context, "Unable to get sysvar {}", id); - InstructionError::UnsupportedSysvar - }) -} - /// Compute meter pub trait ComputeMeter { /// Consume compute units