Refactor: Cleanup InvokeContext (#20785)

* Move blockhash and fee_calculator in ThisInvokeContext instead of using a reference.

* Moves tx_wide_compute_cap into InvokeContext::push().

* Adds ThisInvokeContext::new_mock() constructor.

* Adds missing loader account in uses of MockInvokeContext.

* Use keyed_account_at_index() when accessing keyed_accounts.

* Makes sysvar interface consistent between ThisInvokeContext and MockInvokeContext,
in order to add InvokeContext::get_sysvars().

* Adds InvokeContext::set_blockhash() and InvokeContext ::set_fee_calculator().

* Adds new_mock_with_features.

* Makes ancestors optional in ThisInvokeContext.

* Adds prepare_mock_invoke_context() and mock_process_instruction().
This commit is contained in:
Alexander Meißner 2021-10-21 20:57:42 +02:00 committed by GitHub
parent 0ac89841bf
commit 97c2732d02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 350 additions and 248 deletions

View File

@ -9,6 +9,7 @@ use solana_sdk::{
}, },
ic_msg, ic_msg,
instruction::{Instruction, InstructionError}, instruction::{Instruction, InstructionError},
keyed_account::keyed_account_at_index,
message::Message, message::Message,
process_instruction::{Executor, InvokeContext, ProcessInstructionWithContext}, process_instruction::{Executor, InvokeContext, ProcessInstructionWithContext},
pubkey::Pubkey, pubkey::Pubkey,
@ -355,7 +356,8 @@ impl InstructionProcessor {
instruction_data: &[u8], instruction_data: &[u8],
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if let Some(root_account) = invoke_context.get_keyed_accounts()?.iter().next() { let keyed_accounts = invoke_context.get_keyed_accounts()?;
if let Ok(root_account) = keyed_account_at_index(keyed_accounts, 0) {
let root_id = root_account.unsigned_key(); let root_id = root_account.unsigned_key();
let owner_id = &root_account.owner()?; let owner_id = &root_account.owner()?;
if solana_sdk::native_loader::check_id(owner_id) { if solana_sdk::native_loader::check_id(owner_id) {
@ -536,7 +538,8 @@ impl InstructionProcessor {
caller_write_privileges = Vec::with_capacity(1 + keyed_account_indices_obsolete.len()); caller_write_privileges = Vec::with_capacity(1 + keyed_account_indices_obsolete.len());
caller_write_privileges.push(false); caller_write_privileges.push(false);
for index in keyed_account_indices_obsolete.iter() { for index in keyed_account_indices_obsolete.iter() {
caller_write_privileges.push(caller_keyed_accounts[*index].is_writable()); caller_write_privileges
.push(keyed_account_at_index(caller_keyed_accounts, *index)?.is_writable());
} }
}; };
let mut account_indices = Vec::with_capacity(message.account_keys.len()); let mut account_indices = Vec::with_capacity(message.account_keys.len());

View File

@ -33,7 +33,7 @@ use {
message::Message, message::Message,
native_token::sol_to_lamports, native_token::sol_to_lamports,
poh_config::PohConfig, poh_config::PohConfig,
process_instruction::{stable_log, InvokeContext, ProcessInstructionWithContext}, process_instruction::{self, stable_log, InvokeContext, ProcessInstructionWithContext},
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR}, program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
@ -204,24 +204,6 @@ fn get_sysvar<T: Default + Sysvar + Sized + serde::de::DeserializeOwned>(
var_addr: *mut u8, var_addr: *mut u8,
) -> u64 { ) -> u64 {
let invoke_context = get_invoke_context(); let invoke_context = get_invoke_context();
let sysvar_data = match invoke_context.get_sysvar_data(id).ok_or_else(|| {
ic_msg!(invoke_context, "Unable to get Sysvar {}", id);
UNSUPPORTED_SYSVAR
}) {
Ok(sysvar_data) => sysvar_data,
Err(err) => return err,
};
let var: T = match bincode::deserialize(&sysvar_data) {
Ok(sysvar_data) => sysvar_data,
Err(_) => return UNSUPPORTED_SYSVAR,
};
unsafe {
*(var_addr as *mut _ as *mut T) = var;
}
if invoke_context if invoke_context
.get_compute_meter() .get_compute_meter()
.try_borrow_mut() .try_borrow_mut()
@ -233,7 +215,13 @@ fn get_sysvar<T: Default + Sysvar + Sized + serde::de::DeserializeOwned>(
panic!("Exceeded compute budget"); panic!("Exceeded compute budget");
} }
SUCCESS match process_instruction::get_sysvar::<T>(invoke_context, id) {
Ok(sysvar_data) => unsafe {
*(var_addr as *mut _ as *mut T) = sysvar_data;
SUCCESS
},
Err(_) => UNSUPPORTED_SYSVAR,
}
} }
struct SyscallStubs {} struct SyscallStubs {}

View File

@ -94,8 +94,28 @@ fn bench_program_alu(bencher: &mut Bencher) {
.write_u64::<LittleEndian>(ARMSTRONG_LIMIT) .write_u64::<LittleEndian>(ARMSTRONG_LIMIT)
.unwrap(); .unwrap();
inner_iter.write_u64::<LittleEndian>(0).unwrap(); inner_iter.write_u64::<LittleEndian>(0).unwrap();
let loader_id = bpf_loader::id(); let loader_id = bpf_loader::id();
let mut invoke_context = MockInvokeContext::new(&Pubkey::default(), vec![]); 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 elf = load_elf("bench_alu").unwrap();
let mut executable = <dyn Executable<BpfError, ThisInstructionMeter>>::from_elf( let mut executable = <dyn Executable<BpfError, ThisInstructionMeter>>::from_elf(
@ -254,29 +274,35 @@ fn bench_create_vm(bencher: &mut Bencher) {
fn bench_instruction_count_tuner(_bencher: &mut Bencher) { fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
const BUDGET: u64 = 200_000; const BUDGET: u64 = 200_000;
let loader_id = bpf_loader::id(); let loader_id = bpf_loader::id();
let program_id = solana_sdk::pubkey::new_rand();
let accounts = [RefCell::new(AccountSharedData::new( let accounts = [
1, (
10000001, program_id,
&solana_sdk::pubkey::new_rand(), RefCell::new(AccountSharedData::new(0, 0, &loader_id)),
))]; ),
let keys = [solana_sdk::pubkey::new_rand()]; (
let keyed_accounts: Vec<_> = keys solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(
1,
10000001,
&solana_sdk::pubkey::new_rand(),
)),
),
];
let keyed_accounts: Vec<_> = accounts
.iter() .iter()
.zip(&accounts)
.map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account)) .map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account))
.collect(); .collect();
let instruction_data = vec![0u8]; let instruction_data = vec![0u8];
let mut invoke_context = MockInvokeContext::new(&program_id, keyed_accounts);
let mut invoke_context = MockInvokeContext::new(&loader_id, keyed_accounts);
invoke_context.compute_meter.remaining = BUDGET; invoke_context.compute_meter.remaining = BUDGET;
// Serialize account data // Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters( let (mut serialized, account_lengths) = serialize_parameters(
&loader_id, &loader_id,
&solana_sdk::pubkey::new_rand(), &program_id,
keyed_accounts, &invoke_context.get_keyed_accounts().unwrap()[1..],
&instruction_data, &instruction_data,
) )
.unwrap(); .unwrap();

View File

@ -192,24 +192,23 @@ fn upgrade_bpf_program(
fn run_program( fn run_program(
name: &str, name: &str,
loader_id: &Pubkey,
program_id: &Pubkey, program_id: &Pubkey,
parameter_accounts: Vec<KeyedAccount>, parameter_accounts: Vec<KeyedAccount>,
instruction_data: &[u8], instruction_data: &[u8],
) -> Result<u64, InstructionError> { ) -> Result<u64, InstructionError> {
let path = create_bpf_path(name); let mut file = File::open(create_bpf_path(name)).unwrap();
let mut file = File::open(path).unwrap();
let mut data = vec![]; let mut data = vec![];
file.read_to_end(&mut data).unwrap(); file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id();
let mut invoke_context = MockInvokeContext::new(&program_id, parameter_accounts);
let (parameter_bytes, account_lengths) = serialize_parameters( let (parameter_bytes, account_lengths) = serialize_parameters(
&loader_id, &loader_id,
program_id, program_id,
&parameter_accounts, &invoke_context.get_keyed_accounts().unwrap()[1..],
&instruction_data, &instruction_data,
) )
.unwrap(); .unwrap();
let mut invoke_context = MockInvokeContext::new(&loader_id, parameter_accounts);
let compute_meter = invoke_context.get_compute_meter(); let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter }; let mut instruction_meter = ThisInstructionMeter { compute_meter };
@ -1405,11 +1404,17 @@ fn assert_instruction_count() {
let mut passed = true; let mut passed = true;
println!("\n {:36} expected actual diff", "BPF program"); println!("\n {:36} expected actual diff", "BPF program");
for program in programs.iter() { for program in programs.iter() {
let loader_id = bpf_loader::id();
let program_id = Pubkey::new_unique(); let program_id = Pubkey::new_unique();
let key = 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 mut account = RefCell::new(AccountSharedData::default());
let parameter_accounts = vec![KeyedAccount::new(&key, false, &mut account)]; let parameter_accounts = vec![
let count = run_program(program.0, &program_id, parameter_accounts, &[]).unwrap(); 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 diff: i64 = count as i64 - program.1 as i64; let diff: i64 = count as i64 - program.1 as i64;
println!( println!(
" {:36} {:8} {:6} {:+5} ({:+3.0}%)", " {:36} {:8} {:6} {:+5} ({:+3.0}%)",

View File

@ -3335,7 +3335,8 @@ mod tests {
let mut data = vec![]; let mut data = vec![];
bincode::serialize_into(&mut data, &src_clock).unwrap(); bincode::serialize_into(&mut data, &src_clock).unwrap();
invoke_context invoke_context
.sysvars .get_sysvars()
.borrow_mut()
.push((sysvar::clock::id(), Some(Rc::new(data)))); .push((sysvar::clock::id(), Some(Rc::new(data))));
let mut syscall = SyscallGetClockSysvar { let mut syscall = SyscallGetClockSysvar {
@ -3380,7 +3381,8 @@ mod tests {
let mut data = vec![]; let mut data = vec![];
bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); bincode::serialize_into(&mut data, &src_epochschedule).unwrap();
invoke_context invoke_context
.sysvars .get_sysvars()
.borrow_mut()
.push((sysvar::epoch_schedule::id(), Some(Rc::new(data)))); .push((sysvar::epoch_schedule::id(), Some(Rc::new(data))));
let mut syscall = SyscallGetEpochScheduleSysvar { let mut syscall = SyscallGetEpochScheduleSysvar {
@ -3432,7 +3434,8 @@ mod tests {
let mut data = vec![]; let mut data = vec![];
bincode::serialize_into(&mut data, &src_fees).unwrap(); bincode::serialize_into(&mut data, &src_fees).unwrap();
invoke_context invoke_context
.sysvars .get_sysvars()
.borrow_mut()
.push((sysvar::fees::id(), Some(Rc::new(data)))); .push((sysvar::fees::id(), Some(Rc::new(data))));
let mut syscall = SyscallGetFeesSysvar { let mut syscall = SyscallGetFeesSysvar {
@ -3475,7 +3478,8 @@ mod tests {
let mut data = vec![]; let mut data = vec![];
bincode::serialize_into(&mut data, &src_rent).unwrap(); bincode::serialize_into(&mut data, &src_rent).unwrap();
invoke_context invoke_context
.sysvars .get_sysvars()
.borrow_mut()
.push((sysvar::rent::id(), Some(Rc::new(data)))); .push((sysvar::rent::id(), Some(Rc::new(data))));
let mut syscall = SyscallGetRentSysvar { let mut syscall = SyscallGetRentSysvar {

View File

@ -54,20 +54,19 @@ pub fn process_instruction(
} }
let mut counter = 0; let mut counter = 0;
let mut keyed_accounts_iter = keyed_accounts.iter().skip(2);
for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) {
counter += 1; counter += 1;
if signer != config_keyed_account.unsigned_key() { if signer != config_keyed_account.unsigned_key() {
let signer_account = keyed_accounts_iter.next(); let signer_account =
if signer_account.is_none() { keyed_account_at_index(keyed_accounts, counter + 1).map_err(|_| {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"account {:?} is not in account list", "account {:?} is not in account list",
signer signer,
); );
return Err(InstructionError::MissingRequiredSignature); InstructionError::MissingRequiredSignature
} })?;
let signer_key = signer_account.unwrap().signer_key(); let signer_key = signer_account.signer_key();
if signer_key.is_none() { if signer_key.is_none() {
ic_msg!( ic_msg!(
invoke_context, invoke_context,

View File

@ -37,7 +37,7 @@ pub fn process_instruction(
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
let signers = get_signers(&keyed_accounts[1..]); let signers = get_signers(&keyed_accounts[first_instruction_account..]);
match limited_deserialize(data)? { match limited_deserialize(data)? {
StakeInstruction::Initialize(authorized, lockup) => me.initialize( StakeInstruction::Initialize(authorized, lockup) => me.initialize(
&authorized, &authorized,
@ -330,7 +330,7 @@ mod tests {
account::{self, Account, AccountSharedData, WritableAccount}, account::{self, Account, AccountSharedData, WritableAccount},
instruction::{AccountMeta, Instruction}, instruction::{AccountMeta, Instruction},
keyed_account::create_keyed_accounts_unified, keyed_account::create_keyed_accounts_unified,
process_instruction::{mock_set_sysvar, MockInvokeContext}, process_instruction::MockInvokeContext,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
stake::{ stake::{
@ -338,7 +338,7 @@ mod tests {
instruction::{self, LockupArgs}, instruction::{self, LockupArgs},
state::{Authorized, Lockup, StakeAuthorize}, state::{Authorized, Lockup, StakeAuthorize},
}, },
sysvar::stake_history::StakeHistory, sysvar::{stake_history::StakeHistory, Sysvar},
}; };
use std::{cell::RefCell, rc::Rc, str::FromStr}; use std::{cell::RefCell, rc::Rc, str::FromStr};
@ -442,12 +442,12 @@ mod tests {
&processor_id, &processor_id,
create_keyed_accounts_unified(&keyed_accounts), create_keyed_accounts_unified(&keyed_accounts),
); );
mock_set_sysvar( let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of());
&mut invoke_context, bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap();
sysvar::clock::id(), invoke_context
sysvar::clock::Clock::default(), .get_sysvars()
) .borrow_mut()
.unwrap(); .push((sysvar::clock::id(), Some(Rc::new(data))));
super::process_instruction(1, &instruction.data, &mut invoke_context) super::process_instruction(1, &instruction.data, &mut invoke_context)
} }
} }
@ -1100,11 +1100,11 @@ mod tests {
]; ];
let mut invoke_context = let mut invoke_context =
MockInvokeContext::new(&id(), create_keyed_accounts_unified(&keyed_accounts)); MockInvokeContext::new(&id(), create_keyed_accounts_unified(&keyed_accounts));
let clock = Clock::default(); let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of());
let mut data = vec![]; bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap();
bincode::serialize_into(&mut data, &clock).unwrap();
invoke_context invoke_context
.sysvars .get_sysvars()
.borrow_mut()
.push((sysvar::clock::id(), Some(Rc::new(data)))); .push((sysvar::clock::id(), Some(Rc::new(data))));
assert_eq!( assert_eq!(

View File

@ -322,7 +322,7 @@ pub fn process_instruction(
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
let signers: HashSet<Pubkey> = get_signers(&keyed_accounts[1..]); let signers: HashSet<Pubkey> = get_signers(&keyed_accounts[first_instruction_account..]);
match limited_deserialize(data)? { match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => { VoteInstruction::InitializeAccount(vote_init) => {
verify_rent_exemption( verify_rent_exemption(

View File

@ -6332,6 +6332,7 @@ pub(crate) mod tests {
genesis_config::create_genesis_config, genesis_config::create_genesis_config,
hash, hash,
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
keyed_account::keyed_account_at_index,
message::{Message, MessageHeader}, message::{Message, MessageHeader},
nonce, nonce,
poh_config::PohConfig, poh_config::PohConfig,
@ -6713,11 +6714,11 @@ pub(crate) mod tests {
if let Ok(instruction) = bincode::deserialize(data) { if let Ok(instruction) = bincode::deserialize(data) {
match instruction { match instruction {
MockInstruction::Deduction => { MockInstruction::Deduction => {
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.account .account
.borrow_mut() .borrow_mut()
.checked_add_lamports(1)?; .checked_add_lamports(1)?;
keyed_accounts[first_instruction_account + 2] keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
.account .account
.borrow_mut() .borrow_mut()
.checked_sub_lamports(1)?; .checked_sub_lamports(1)?;
@ -11295,18 +11296,16 @@ pub(crate) mod tests {
) -> result::Result<(), InstructionError> { ) -> result::Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
let lamports = data[0] as u64; let lamports = data[0] as u64;
{ keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
let mut to_account =
keyed_accounts[first_instruction_account + 1].try_account_ref_mut()?;
let mut dup_account =
keyed_accounts[first_instruction_account + 2].try_account_ref_mut()?;
dup_account.checked_sub_lamports(lamports)?;
to_account.checked_add_lamports(lamports)?;
}
keyed_accounts[first_instruction_account]
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_sub_lamports(lamports)?; .checked_sub_lamports(lamports)?;
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?
.checked_sub_lamports(lamports)?;
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_add_lamports(lamports)?; .checked_add_lamports(lamports)?;
Ok(()) Ok(())
@ -11790,14 +11789,9 @@ pub(crate) mod tests {
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
) -> result::Result<(), InstructionError> { ) -> result::Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
assert_eq!( let account = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
42, assert_eq!(42, account.lamports().unwrap());
keyed_accounts[first_instruction_account] account.try_account_ref_mut()?.checked_add_lamports(1)?;
.lamports()
.unwrap()
);
let mut account = keyed_accounts[first_instruction_account].try_account_ref_mut()?;
account.checked_add_lamports(1)?;
Ok(()) Ok(())
} }
@ -14598,8 +14592,8 @@ pub(crate) mod tests {
) -> std::result::Result<(), InstructionError> { ) -> std::result::Result<(), InstructionError> {
use solana_sdk::account::WritableAccount; use solana_sdk::account::WritableAccount;
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
let mut data = keyed_accounts[first_instruction_account + 1].try_account_ref_mut()?; let data = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
data.data_as_mut_slice()[0] = 5; data.try_account_ref_mut()?.data_as_mut_slice()[0] = 5;
Ok(()) Ok(())
} }

View File

@ -17,7 +17,7 @@ use solana_sdk::{
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
hash::Hash, hash::Hash,
ic_logger_msg, ic_logger_msg,
instruction::{CompiledInstruction, Instruction, InstructionError}, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, KeyedAccount}, keyed_account::{create_keyed_accounts_unified, KeyedAccount},
message::Message, message::Message,
precompiles::is_precompile, precompiles::is_precompile,
@ -48,6 +48,32 @@ impl ComputeMeter for ThisComputeMeter {
self.remaining self.remaining
} }
} }
impl ThisComputeMeter {
pub fn new_ref(remaining: u64) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self { remaining }))
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
}
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<LogCollector>>) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self { log_collector }))
}
}
pub struct ThisInvokeContext<'a> { pub struct ThisInvokeContext<'a> {
instruction_index: usize, instruction_index: usize,
invoke_stack: Vec<InvokeContextStackFrame<'a>>, invoke_stack: Vec<InvokeContextStackFrame<'a>>,
@ -63,11 +89,11 @@ pub struct ThisInvokeContext<'a> {
feature_set: Arc<FeatureSet>, feature_set: Arc<FeatureSet>,
pub timings: ExecuteDetailsTimings, pub timings: ExecuteDetailsTimings,
account_db: Arc<Accounts>, account_db: Arc<Accounts>,
ancestors: &'a Ancestors, ancestors: Option<&'a Ancestors>,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>, sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
blockhash: &'a Hash, blockhash: Hash,
fee_calculator: &'a FeeCalculator, fee_calculator: FeeCalculator,
return_data: (Pubkey, Vec<u8>), return_data: (Pubkey, Vec<u8>),
} }
impl<'a> ThisInvokeContext<'a> { impl<'a> ThisInvokeContext<'a> {
@ -83,9 +109,9 @@ impl<'a> ThisInvokeContext<'a> {
instruction_recorders: Option<&'a [InstructionRecorder]>, instruction_recorders: Option<&'a [InstructionRecorder]>,
feature_set: Arc<FeatureSet>, feature_set: Arc<FeatureSet>,
account_db: Arc<Accounts>, account_db: Arc<Accounts>,
ancestors: &'a Ancestors, ancestors: Option<&'a Ancestors>,
blockhash: &'a Hash, blockhash: Hash,
fee_calculator: &'a FeeCalculator, fee_calculator: FeeCalculator,
) -> Self { ) -> Self {
Self { Self {
instruction_index: 0, instruction_index: 0,
@ -94,7 +120,7 @@ impl<'a> ThisInvokeContext<'a> {
pre_accounts: Vec::new(), pre_accounts: Vec::new(),
accounts, accounts,
programs, programs,
logger: Rc::new(RefCell::new(ThisLogger { log_collector })), logger: ThisLogger::new_ref(log_collector),
compute_budget, compute_budget,
compute_meter, compute_meter,
executors, executors,
@ -103,12 +129,41 @@ impl<'a> ThisInvokeContext<'a> {
timings: ExecuteDetailsTimings::default(), timings: ExecuteDetailsTimings::default(),
account_db, account_db,
ancestors, ancestors,
sysvars: RefCell::new(vec![]), sysvars: RefCell::new(Vec::new()),
blockhash, blockhash,
fee_calculator, fee_calculator,
return_data: (Pubkey::default(), Vec::new()), return_data: (Pubkey::default(), Vec::new()),
} }
} }
pub fn new_mock_with_features(
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
feature_set: Arc<FeatureSet>,
) -> Self {
Self::new(
Rent::default(),
accounts,
programs,
None,
ComputeBudget::default(),
ThisComputeMeter::new_ref(std::i64::MAX as u64),
Rc::new(RefCell::new(Executors::default())),
None,
feature_set,
Arc::new(Accounts::default_for_tests()),
None,
Hash::default(),
FeeCalculator::default(),
)
}
pub fn new_mock(
accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
programs: &'a [(Pubkey, ProcessInstructionWithContext)],
) -> Self {
Self::new_mock_with_features(accounts, programs, Arc::new(FeatureSet::all_enabled()))
}
} }
impl<'a> InvokeContext for ThisInvokeContext<'a> { impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push( fn push(
@ -140,6 +195,10 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
} }
if self.invoke_stack.is_empty() { 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()); self.pre_accounts = Vec::with_capacity(instruction.accounts.len());
let mut work = |_unique_index: usize, account_index: usize| { let mut work = |_unique_index: usize, account_index: usize| {
if account_index < self.accounts.len() { if account_index < self.accounts.len() {
@ -364,11 +423,6 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
self.executors.borrow().get(pubkey) self.executors.borrow().get(pubkey)
} }
fn set_instruction_index(&mut self, instruction_index: usize) { fn set_instruction_index(&mut self, instruction_index: usize) {
if !self.feature_set.is_active(&tx_wide_compute_cap::id()) {
self.compute_meter = Rc::new(RefCell::new(ThisComputeMeter {
remaining: self.compute_budget.max_units,
}));
}
self.instruction_index = instruction_index; self.instruction_index = instruction_index;
} }
fn record_instruction(&self, instruction: &Instruction) { fn record_instruction(&self, instruction: &Instruction) {
@ -399,6 +453,10 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
self.timings.execute_us += execute_us; self.timings.execute_us += execute_us;
self.timings.deserialize_us += deserialize_us; self.timings.deserialize_us += deserialize_us;
} }
#[allow(clippy::type_complexity)]
fn get_sysvars(&self) -> &RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>> {
&self.sysvars
}
fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> { fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() { if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() {
// Try share from cache // Try share from cache
@ -406,13 +464,15 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
.iter() .iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }); .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
if result.is_none() { if result.is_none() {
// Load it if let Some(ancestors) = self.ancestors {
result = self // Load it
.account_db result = self
.load_with_fixed_root(self.ancestors, id) .account_db
.map(|(account, _)| Rc::new(account.data().to_vec())); .load_with_fixed_root(ancestors, id)
// Cache it .map(|(account, _)| Rc::new(account.data().to_vec()));
sysvars.push((*id, result.clone())); // Cache it
sysvars.push((*id, result.clone()));
}
} }
result result
} else { } else {
@ -422,11 +482,17 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn get_compute_budget(&self) -> &ComputeBudget { fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget &self.compute_budget
} }
fn set_blockhash(&mut self, hash: Hash) {
self.blockhash = hash;
}
fn get_blockhash(&self) -> &Hash { fn get_blockhash(&self) -> &Hash {
self.blockhash &self.blockhash
}
fn set_fee_calculator(&mut self, fee_calculator: FeeCalculator) {
self.fee_calculator = fee_calculator;
} }
fn get_fee_calculator(&self) -> &FeeCalculator { fn get_fee_calculator(&self) -> &FeeCalculator {
self.fee_calculator &self.fee_calculator
} }
fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> { fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
self.return_data = (*self.get_caller()?, data); self.return_data = (*self.get_caller()?, data);
@ -436,21 +502,90 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
(self.return_data.0, &self.return_data.1) (self.return_data.0, &self.return_data.1)
} }
} }
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>, pub struct MockInvokeContextPreparation {
pub accounts: Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
pub message: Message,
pub account_indices: Vec<usize>,
} }
impl Logger for ThisLogger {
fn log_enabled(&self) -> bool { pub fn prepare_mock_invoke_context(
log_enabled!(log::Level::Info) || self.log_collector.is_some() program_indices: &[usize],
instruction_data: &[u8],
keyed_accounts: &[(bool, bool, Pubkey, Rc<RefCell<AccountSharedData>>)],
) -> MockInvokeContextPreparation {
#[allow(clippy::type_complexity)]
let (accounts, mut metas): (
Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
Vec<AccountMeta>,
) = 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);
} }
fn log(&self, message: &str) { let message = Message::new(
debug!("{}", message); &[Instruction::new_with_bytes(
if let Some(log_collector) = &self.log_collector { program_id,
log_collector.log(message); instruction_data,
} metas,
)],
None,
);
let account_indices: Vec<usize> = 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 mock_process_instruction(
loader_id: &Pubkey,
mut program_indices: Vec<usize>,
instruction_data: &[u8],
keyed_accounts: &[(bool, bool, Pubkey, Rc<RefCell<AccountSharedData>>)],
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)] #[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct MessageProcessor {} pub struct MessageProcessor {}
@ -500,9 +635,9 @@ impl MessageProcessor {
instruction_recorders, instruction_recorders,
feature_set, feature_set,
account_db, account_db,
ancestors, Some(ancestors),
&blockhash, blockhash,
&fee_calculator, fee_calculator,
); );
let compute_meter = invoke_context.get_compute_meter(); let compute_meter = invoke_context.get_compute_meter();
@ -585,9 +720,9 @@ mod tests {
use super::*; use super::*;
use solana_sdk::{ use solana_sdk::{
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
keyed_account::keyed_account_at_index,
message::Message, message::Message,
native_loader::{self, create_loadable_account_for_test}, native_loader::{self, create_loadable_account_for_test},
process_instruction::MockComputeMeter,
secp256k1_instruction::new_secp256k1_instruction, secp256k1_instruction::new_secp256k1_instruction,
secp256k1_program, secp256k1_program,
}; };
@ -610,11 +745,11 @@ mod tests {
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
assert_eq!( assert_eq!(
*program_id, *program_id,
keyed_accounts[first_instruction_account].owner()? keyed_account_at_index(keyed_accounts, first_instruction_account)?.owner()?
); );
assert_ne!( assert_ne!(
keyed_accounts[first_instruction_account + 1].owner()?, keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.owner()?,
*keyed_accounts[first_instruction_account].unsigned_key() *keyed_account_at_index(keyed_accounts, first_instruction_account)?.unsigned_key()
); );
if let Ok(instruction) = bincode::deserialize(data) { if let Ok(instruction) = bincode::deserialize(data) {
@ -622,17 +757,17 @@ mod tests {
MockInstruction::NoopSuccess => (), MockInstruction::NoopSuccess => (),
MockInstruction::NoopFail => return Err(InstructionError::GenericError), MockInstruction::NoopFail => return Err(InstructionError::GenericError),
MockInstruction::ModifyOwned => { MockInstruction::ModifyOwned => {
keyed_accounts[first_instruction_account] keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()? .try_account_ref_mut()?
.data_as_mut_slice()[0] = 1 .data_as_mut_slice()[0] = 1
} }
MockInstruction::ModifyNotOwned => { MockInstruction::ModifyNotOwned => {
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.try_account_ref_mut()? .try_account_ref_mut()?
.data_as_mut_slice()[0] = 1 .data_as_mut_slice()[0] = 1
} }
MockInstruction::ModifyReadonly => { MockInstruction::ModifyReadonly => {
keyed_accounts[first_instruction_account + 2] keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
.try_account_ref_mut()? .try_account_ref_mut()?
.data_as_mut_slice()[0] = 1 .data_as_mut_slice()[0] = 1
} }
@ -678,24 +813,7 @@ mod tests {
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)], &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
None, None,
); );
let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]);
let blockhash = Hash::default();
let fee_calculator = FeeCalculator::default();
let mut invoke_context = ThisInvokeContext::new(
Rent::default(),
&accounts,
&[],
None,
ComputeBudget::default(),
Rc::new(RefCell::new(MockComputeMeter::default())),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
Arc::new(Accounts::default_for_tests()),
&ancestors,
&blockhash,
&fee_calculator,
);
// Check call depth increases and has a limit // Check call depth increases and has a limit
let mut depth_reached = 0; let mut depth_reached = 0;
@ -782,24 +900,7 @@ mod tests {
)], )],
None, None,
); );
let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new_mock(&accounts, &[]);
let blockhash = Hash::default();
let fee_calculator = FeeCalculator::default();
let mut invoke_context = ThisInvokeContext::new(
Rent::default(),
&accounts,
&[],
None,
ComputeBudget::default(),
Rc::new(RefCell::new(MockComputeMeter::default())),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
Arc::new(Accounts::default_for_tests()),
&ancestors,
&blockhash,
&fee_calculator,
);
invoke_context invoke_context
.push(&message, &message.instructions[0], &[0], None) .push(&message, &message.instructions[0], &[0], None)
.unwrap(); .unwrap();
@ -833,11 +934,11 @@ mod tests {
match instruction { match instruction {
MockSystemInstruction::Correct => Ok(()), MockSystemInstruction::Correct => Ok(()),
MockSystemInstruction::AttemptCredit { lamports } => { MockSystemInstruction::AttemptCredit { lamports } => {
keyed_accounts[first_instruction_account] keyed_account_at_index(keyed_accounts, first_instruction_account)?
.account .account
.borrow_mut() .borrow_mut()
.checked_sub_lamports(lamports)?; .checked_sub_lamports(lamports)?;
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.account .account
.borrow_mut() .borrow_mut()
.checked_add_lamports(lamports)?; .checked_add_lamports(lamports)?;
@ -845,7 +946,7 @@ mod tests {
} }
// Change data in a read-only account // Change data in a read-only account
MockSystemInstruction::AttemptDataChange { data } => { MockSystemInstruction::AttemptDataChange { data } => {
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.account .account
.borrow_mut() .borrow_mut()
.set_data(vec![data]); .set_data(vec![data]);
@ -905,7 +1006,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -936,7 +1037,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -971,7 +1072,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -1006,9 +1107,11 @@ mod tests {
match instruction { match instruction {
MockSystemInstruction::BorrowFail => { MockSystemInstruction::BorrowFail => {
let from_account = let from_account =
keyed_accounts[first_instruction_account].try_account_ref_mut()?; keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?;
let dup_account = let dup_account =
keyed_accounts[first_instruction_account + 2].try_account_ref_mut()?; keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
.try_account_ref_mut()?;
if from_account.lamports() != dup_account.lamports() { if from_account.lamports() != dup_account.lamports() {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -1017,12 +1120,16 @@ mod tests {
MockSystemInstruction::MultiBorrowMut => { MockSystemInstruction::MultiBorrowMut => {
let from_lamports = { let from_lamports = {
let from_account = let from_account =
keyed_accounts[first_instruction_account].try_account_ref_mut()?; keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?;
from_account.lamports() from_account.lamports()
}; };
let dup_lamports = { let dup_lamports = {
let dup_account = keyed_accounts[first_instruction_account + 2] let dup_account = keyed_account_at_index(
.try_account_ref_mut()?; keyed_accounts,
first_instruction_account + 2,
)?
.try_account_ref_mut()?;
dup_account.lamports() dup_account.lamports()
}; };
if from_lamports != dup_lamports { if from_lamports != dup_lamports {
@ -1032,18 +1139,24 @@ mod tests {
} }
MockSystemInstruction::DoWork { lamports, data } => { MockSystemInstruction::DoWork { lamports, data } => {
{ {
let mut to_account = keyed_accounts[first_instruction_account + 1] let mut to_account = keyed_account_at_index(
.try_account_ref_mut()?; keyed_accounts,
let mut dup_account = keyed_accounts[first_instruction_account + 2] first_instruction_account + 1,
.try_account_ref_mut()?; )?
.try_account_ref_mut()?;
let mut dup_account = keyed_account_at_index(
keyed_accounts,
first_instruction_account + 2,
)?
.try_account_ref_mut()?;
dup_account.checked_sub_lamports(lamports)?; dup_account.checked_sub_lamports(lamports)?;
to_account.checked_add_lamports(lamports)?; to_account.checked_add_lamports(lamports)?;
dup_account.set_data(vec![data]); dup_account.set_data(vec![data]);
} }
keyed_accounts[first_instruction_account] keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_sub_lamports(lamports)?; .checked_sub_lamports(lamports)?;
keyed_accounts[first_instruction_account + 1] keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.try_account_ref_mut()? .try_account_ref_mut()?
.checked_add_lamports(lamports)?; .checked_add_lamports(lamports)?;
Ok(()) Ok(())
@ -1104,7 +1217,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -1139,7 +1252,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -1172,7 +1285,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&ancestors, &ancestors,
@ -1233,24 +1346,7 @@ mod tests {
); );
let message = Message::new(&[callee_instruction], None); let message = Message::new(&[callee_instruction], None);
let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice());
let blockhash = Hash::default();
let fee_calculator = FeeCalculator::default();
let mut invoke_context = ThisInvokeContext::new(
Rent::default(),
&accounts,
programs.as_slice(),
None,
ComputeBudget::default(),
Rc::new(RefCell::new(MockComputeMeter::default())),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
Arc::new(Accounts::default_for_tests()),
&ancestors,
&blockhash,
&fee_calculator,
);
invoke_context invoke_context
.push(&message, &caller_instruction, &program_indices[..1], None) .push(&message, &caller_instruction, &program_indices[..1], None)
.unwrap(); .unwrap();
@ -1378,24 +1474,7 @@ mod tests {
); );
let message = Message::new(&[callee_instruction.clone()], None); let message = Message::new(&[callee_instruction.clone()], None);
let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new_mock(&accounts, programs.as_slice());
let blockhash = Hash::default();
let fee_calculator = FeeCalculator::default();
let mut invoke_context = ThisInvokeContext::new(
Rent::default(),
&accounts,
programs.as_slice(),
None,
ComputeBudget::default(),
Rc::new(RefCell::new(MockComputeMeter::default())),
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
Arc::new(Accounts::default_for_tests()),
&ancestors,
&blockhash,
&fee_calculator,
);
invoke_context invoke_context
.push(&message, &caller_instruction, &program_indices, None) .push(&message, &caller_instruction, &program_indices, None)
.unwrap(); .unwrap();
@ -1509,7 +1588,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
Rc::new(RefCell::new(MockComputeMeter::default())), ThisComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(), &mut ExecuteDetailsTimings::default(),
Arc::new(Accounts::default_for_tests()), Arc::new(Accounts::default_for_tests()),
&Ancestors::default(), &Ancestors::default(),

View File

@ -121,12 +121,19 @@ pub trait InvokeContext {
execute_us: u64, execute_us: u64,
deserialize_us: u64, deserialize_us: u64,
); );
/// Get sysvars
#[allow(clippy::type_complexity)]
fn get_sysvars(&self) -> &RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>;
/// Get sysvar data /// Get sysvar data
fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>>; fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>>;
/// Get this invocation's compute budget /// Get this invocation's compute budget
fn get_compute_budget(&self) -> &ComputeBudget; fn get_compute_budget(&self) -> &ComputeBudget;
/// Set this invocation's blockhash
fn set_blockhash(&mut self, hash: Hash);
/// Get this invocation's blockhash /// Get this invocation's blockhash
fn get_blockhash(&self) -> &Hash; fn get_blockhash(&self) -> &Hash;
/// Set this invocation's `FeeCalculator`
fn set_fee_calculator(&mut self, fee_calculator: FeeCalculator);
/// Get this invocation's `FeeCalculator` /// Get this invocation's `FeeCalculator`
fn get_fee_calculator(&self) -> &FeeCalculator; fn get_fee_calculator(&self) -> &FeeCalculator;
/// Set the return data /// Set the return data
@ -350,7 +357,8 @@ pub struct MockInvokeContext<'a> {
pub compute_meter: MockComputeMeter, pub compute_meter: MockComputeMeter,
pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>, pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
pub accounts: Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>, pub accounts: Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
pub sysvars: Vec<(Pubkey, Option<Rc<Vec<u8>>>)>, #[allow(clippy::type_complexity)]
pub sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
pub disabled_features: HashSet<Pubkey>, pub disabled_features: HashSet<Pubkey>,
pub blockhash: Hash, pub blockhash: Hash,
pub fee_calculator: FeeCalculator, pub fee_calculator: FeeCalculator,
@ -369,7 +377,7 @@ impl<'a> MockInvokeContext<'a> {
}, },
programs: vec![], programs: vec![],
accounts: vec![], accounts: vec![],
sysvars: vec![], sysvars: RefCell::new(Vec::new()),
disabled_features: HashSet::default(), disabled_features: HashSet::default(),
blockhash: Hash::default(), blockhash: Hash::default(),
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
@ -390,21 +398,6 @@ impl<'a> MockInvokeContext<'a> {
} }
} }
pub fn mock_set_sysvar<T: Sysvar>(
mock_invoke_context: &mut MockInvokeContext,
id: Pubkey,
sysvar: T,
) -> Result<(), InstructionError> {
let mut data = Vec::with_capacity(T::size_of());
bincode::serialize_into(&mut data, &sysvar).map_err(|err| {
ic_msg!(mock_invoke_context, "Unable to serialize sysvar: {:?}", err);
InstructionError::GenericError
})?;
mock_invoke_context.sysvars.push((id, Some(Rc::new(data))));
Ok(())
}
impl<'a> InvokeContext for MockInvokeContext<'a> { impl<'a> InvokeContext for MockInvokeContext<'a> {
fn push( fn push(
&mut self, &mut self,
@ -498,17 +491,28 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
_deserialize_us: u64, _deserialize_us: u64,
) { ) {
} }
#[allow(clippy::type_complexity)]
fn get_sysvars(&self) -> &RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>> {
&self.sysvars
}
fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> { fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
self.sysvars self.sysvars
.borrow()
.iter() .iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }) .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None })
} }
fn get_compute_budget(&self) -> &ComputeBudget { fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget &self.compute_budget
} }
fn set_blockhash(&mut self, hash: Hash) {
self.blockhash = hash;
}
fn get_blockhash(&self) -> &Hash { fn get_blockhash(&self) -> &Hash {
&self.blockhash &self.blockhash
} }
fn set_fee_calculator(&mut self, fee_calculator: FeeCalculator) {
self.fee_calculator = fee_calculator;
}
fn get_fee_calculator(&self) -> &FeeCalculator { fn get_fee_calculator(&self) -> &FeeCalculator {
&self.fee_calculator &self.fee_calculator
} }