Cleanup - mock InvokeContext (#31007)

* Turns with_mock_invoke_context() into a macro.

* Removes prepare_mock_invoke_context().

* Replaces InvokeContext::new_mock() with with_mock_invoke_context().

* Removes InvokeContext::new_mock().

* Removes Cow from InvokeContext::sysvar_cache.

* Removes override parameters from mock_process_instruction().

* cargo fmt
This commit is contained in:
Alexander Meißner 2023-04-03 17:23:24 +02:00 committed by GitHub
parent 9219a16c42
commit a0c7fde90e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1013 additions and 1416 deletions

View File

@ -37,6 +37,7 @@ use {
account_utils::StateMut,
bpf_loader, bpf_loader_deprecated,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
feature_set::FeatureSet,
instruction::{Instruction, InstructionError},
loader_instruction,
message::Message,
@ -46,9 +47,7 @@ use {
signature::{keypair_from_seed, read_keypair_file, Keypair, Signature, Signer},
system_instruction::{self, SystemError},
system_program,
sysvar::rent::Rent,
transaction::{Transaction, TransactionError},
transaction_context::TransactionContext,
},
std::{
fs::File,
@ -2018,12 +2017,10 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
let mut program_data = Vec::new();
file.read_to_end(&mut program_data)
.map_err(|err| format!("Unable to read program file: {err}"))?;
let mut transaction_context = TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
// Verify the program
let loader = create_loader(
&invoke_context.feature_set,
&FeatureSet::default(),
&ComputeBudget::default(),
true,
true,

View File

@ -31,7 +31,6 @@ use {
},
std::{
alloc::Layout,
borrow::Cow,
cell::RefCell,
fmt::{self, Debug},
rc::Rc,
@ -116,7 +115,7 @@ pub struct InvokeContext<'a> {
rent: Rent,
pre_accounts: Vec<PreAccount>,
builtin_programs: &'a [BuiltinProgram],
pub sysvar_cache: Cow<'a, SysvarCache>,
sysvar_cache: &'a SysvarCache,
pub trace_log_stack: Vec<TraceLogStackFrame>,
log_collector: Option<Rc<RefCell<LogCollector>>>,
compute_budget: ComputeBudget,
@ -138,7 +137,7 @@ impl<'a> InvokeContext<'a> {
transaction_context: &'a mut TransactionContext,
rent: Rent,
builtin_programs: &'a [BuiltinProgram],
sysvar_cache: Cow<'a, SysvarCache>,
sysvar_cache: &'a SysvarCache,
log_collector: Option<Rc<RefCell<LogCollector>>>,
compute_budget: ComputeBudget,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
@ -169,43 +168,6 @@ impl<'a> InvokeContext<'a> {
}
}
pub fn new_mock(
transaction_context: &'a mut TransactionContext,
builtin_programs: &'a [BuiltinProgram],
) -> Self {
let mut sysvar_cache = SysvarCache::default();
sysvar_cache.fill_missing_entries(|pubkey, callback| {
for index in 0..transaction_context.get_number_of_accounts() {
if transaction_context
.get_key_of_account_at_index(index)
.unwrap()
== pubkey
{
callback(
transaction_context
.get_account_at_index(index)
.unwrap()
.borrow()
.data(),
);
}
}
});
Self::new(
transaction_context,
Rent::default(),
builtin_programs,
Cow::Owned(sysvar_cache),
Some(LogCollector::new_ref()),
ComputeBudget::default(),
Rc::new(RefCell::new(TransactionExecutorCache::default())),
Arc::new(FeatureSet::all_enabled()),
Hash::default(),
0,
0,
)
}
/// Push a stack frame onto the invocation stack
pub fn push(&mut self) -> Result<(), InstructionError> {
let instruction_context = self
@ -807,7 +769,7 @@ impl<'a> InvokeContext<'a> {
/// Get cached sysvars
pub fn get_sysvar_cache(&self) -> &SysvarCache {
&self.sysvar_cache
self.sysvar_cache
}
// Set this instruction syscall context
@ -881,16 +843,73 @@ impl<'a> InvokeContext<'a> {
}
}
pub struct MockInvokeContextPreparation {
pub transaction_accounts: Vec<TransactionAccount>,
pub instruction_accounts: Vec<InstructionAccount>,
#[macro_export]
macro_rules! with_mock_invoke_context {
($invoke_context:ident, $transaction_context:ident, $transaction_accounts:expr) => {
use {
solana_sdk::{
account::ReadableAccount, feature_set::FeatureSet, hash::Hash, sysvar::rent::Rent,
transaction_context::TransactionContext,
},
std::{cell::RefCell, rc::Rc, sync::Arc},
$crate::{
compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache,
invoke_context::InvokeContext, log_collector::LogCollector,
sysvar_cache::SysvarCache,
},
};
let compute_budget = ComputeBudget::default();
let mut $transaction_context = TransactionContext::new(
$transaction_accounts,
Some(Rent::default()),
compute_budget.max_invoke_stack_height,
compute_budget.max_instruction_trace_length,
);
$transaction_context.enable_cap_accounts_data_allocations_per_transaction();
let mut sysvar_cache = SysvarCache::default();
sysvar_cache.fill_missing_entries(|pubkey, callback| {
for index in 0..$transaction_context.get_number_of_accounts() {
if $transaction_context
.get_key_of_account_at_index(index)
.unwrap()
== pubkey
{
callback(
$transaction_context
.get_account_at_index(index)
.unwrap()
.borrow()
.data(),
);
}
}
});
let mut $invoke_context = InvokeContext::new(
&mut $transaction_context,
Rent::default(),
&[],
&sysvar_cache,
Some(LogCollector::new_ref()),
compute_budget,
Rc::new(RefCell::new(TransactionExecutorCache::default())),
Arc::new(FeatureSet::all_enabled()),
Hash::default(),
0,
0,
);
};
}
pub fn prepare_mock_invoke_context(
transaction_accounts: Vec<TransactionAccount>,
pub fn mock_process_instruction<F: FnMut(&mut InvokeContext)>(
loader_id: &Pubkey,
mut program_indices: Vec<IndexOfAccount>,
instruction_data: &[u8],
mut transaction_accounts: Vec<TransactionAccount>,
instruction_account_metas: Vec<AccountMeta>,
_program_indices: &[IndexOfAccount],
) -> MockInvokeContextPreparation {
expected_result: Result<(), InstructionError>,
process_instruction: ProcessInstructionWithContext,
mut pre_adjustments: F,
) -> Vec<AccountSharedData> {
let mut instruction_accounts: Vec<InstructionAccount> =
Vec::with_capacity(instruction_account_metas.len());
for (instruction_account_index, account_meta) in instruction_account_metas.iter().enumerate() {
@ -915,97 +934,19 @@ pub fn prepare_mock_invoke_context(
is_writable: account_meta.is_writable,
});
}
MockInvokeContextPreparation {
transaction_accounts,
instruction_accounts,
}
}
pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
loader_id: Pubkey,
account_size: usize,
is_writable: bool,
mut callback: F,
) -> R {
let program_indices = vec![0, 1];
let program_key = Pubkey::new_unique();
let transaction_accounts = vec![
(
loader_id,
AccountSharedData::new(0, 0, &native_loader::id()),
),
(program_key, AccountSharedData::new(1, 0, &loader_id)),
(
Pubkey::new_unique(),
AccountSharedData::new(2, account_size, &program_key),
),
];
let instruction_accounts = vec![AccountMeta {
pubkey: transaction_accounts.get(2).unwrap().0,
is_signer: false,
is_writable,
}];
let preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let compute_budget = ComputeBudget::default();
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
Some(Rent::default()),
compute_budget.max_invoke_stack_height,
compute_budget.max_instruction_trace_length,
);
transaction_context.enable_cap_accounts_data_allocations_per_transaction();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(&program_indices, &preparation.instruction_accounts, &[]);
invoke_context.push().unwrap();
callback(&mut invoke_context)
}
pub fn mock_process_instruction(
loader_id: &Pubkey,
mut program_indices: Vec<IndexOfAccount>,
instruction_data: &[u8],
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
sysvar_cache_override: Option<&SysvarCache>,
feature_set_override: Option<Arc<FeatureSet>>,
expected_result: Result<(), InstructionError>,
process_instruction: ProcessInstructionWithContext,
) -> Vec<AccountSharedData> {
program_indices.insert(0, transaction_accounts.len() as IndexOfAccount);
let mut preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let processor_account = AccountSharedData::new(0, 0, &native_loader::id());
preparation
.transaction_accounts
.push((*loader_id, processor_account));
let compute_budget = ComputeBudget::default();
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
Some(Rent::default()),
compute_budget.max_invoke_stack_height,
compute_budget.max_instruction_trace_length,
);
transaction_context.enable_cap_accounts_data_allocations_per_transaction();
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
if let Some(sysvar_cache) = sysvar_cache_override {
invoke_context.sysvar_cache = Cow::Borrowed(sysvar_cache);
}
if let Some(feature_set) = feature_set_override {
invoke_context.feature_set = feature_set;
}
transaction_accounts.push((*loader_id, processor_account));
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
let builtin_programs = &[BuiltinProgram {
program_id: *loader_id,
process_instruction,
}];
invoke_context.builtin_programs = builtin_programs;
pre_adjustments(&mut invoke_context);
let result = invoke_context.process_instruction(
instruction_data,
&preparation.instruction_accounts,
&instruction_accounts,
&program_indices,
&mut 0,
&mut ExecuteTimings::default(),
@ -1171,13 +1112,15 @@ mod tests {
#[test]
fn test_instruction_stack_height() {
const MAX_DEPTH: usize = 10;
let one_more_than_max_depth = ComputeBudget::default()
.max_invoke_stack_height
.saturating_add(1);
let mut invoke_stack = vec![];
let mut accounts = vec![];
let mut transaction_accounts = vec![];
let mut instruction_accounts = vec![];
for index in 0..MAX_DEPTH {
for index in 0..one_more_than_max_depth {
invoke_stack.push(solana_sdk::pubkey::new_rand());
accounts.push((
transaction_accounts.push((
solana_sdk::pubkey::new_rand(),
AccountSharedData::new(index as u64, 1, invoke_stack.get(index).unwrap()),
));
@ -1190,7 +1133,7 @@ mod tests {
});
}
for (index, program_id) in invoke_stack.iter().enumerate() {
accounts.push((
transaction_accounts.push((
*program_id,
AccountSharedData::new(1, 1, &solana_sdk::pubkey::Pubkey::default()),
));
@ -1202,13 +1145,7 @@ mod tests {
is_writable: false,
});
}
let mut transaction_context = TransactionContext::new(
accounts,
Some(Rent::default()),
ComputeBudget::default().max_invoke_stack_height,
MAX_DEPTH,
);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
// Check call depth increases and has a limit
let mut depth_reached = 0;
@ -1218,17 +1155,17 @@ mod tests {
.get_next_instruction_context()
.unwrap()
.configure(
&[(MAX_DEPTH + depth_reached) as IndexOfAccount],
&[one_more_than_max_depth.saturating_add(depth_reached) as IndexOfAccount],
&instruction_accounts,
&[],
);
if Err(InstructionError::CallDepth) == invoke_context.push() {
break;
}
depth_reached += 1;
depth_reached = depth_reached.saturating_add(1);
}
assert_ne!(depth_reached, 0);
assert!(depth_reached < MAX_DEPTH);
assert!(depth_reached < one_more_than_max_depth);
}
#[test]
@ -1249,18 +1186,13 @@ mod tests {
#[test]
fn test_process_instruction() {
let callee_program_id = solana_sdk::pubkey::new_rand();
let builtin_programs = &[BuiltinProgram {
program_id: callee_program_id,
process_instruction: mock_process_instruction,
}];
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![
let transaction_accounts = vec![
(solana_sdk::pubkey::new_rand(), owned_account),
(solana_sdk::pubkey::new_rand(), not_owned_account),
(solana_sdk::pubkey::new_rand(), readonly_account),
@ -1268,9 +1200,9 @@ mod tests {
(solana_sdk::pubkey::new_rand(), loader_account),
];
let metas = vec![
AccountMeta::new(accounts.get(0).unwrap().0, false),
AccountMeta::new(accounts.get(1).unwrap().0, false),
AccountMeta::new_readonly(accounts.get(2).unwrap().0, false),
AccountMeta::new(transaction_accounts.get(0).unwrap().0, false),
AccountMeta::new(transaction_accounts.get(1).unwrap().0, false),
AccountMeta::new_readonly(transaction_accounts.get(2).unwrap().0, false),
];
let instruction_accounts = (0..4)
.map(|instruction_account_index| InstructionAccount {
@ -1281,10 +1213,12 @@ mod tests {
is_writable: instruction_account_index < 2,
})
.collect::<Vec<_>>();
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 2, 18);
let mut invoke_context =
InvokeContext::new_mock(&mut transaction_context, builtin_programs);
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
let builtin_programs = &[BuiltinProgram {
program_id: callee_program_id,
process_instruction: mock_process_instruction,
}];
invoke_context.builtin_programs = builtin_programs;
// Account modification tests
let cases = vec![
@ -1364,7 +1298,7 @@ mod tests {
assert!(compute_units_consumed > 0);
assert_eq!(
compute_units_consumed,
compute_units_to_consume + MOCK_BUILTIN_COMPUTE_UNIT_COST
compute_units_to_consume.saturating_add(MOCK_BUILTIN_COMPUTE_UNIT_COST),
);
assert_eq!(result, expected_result);
@ -1374,11 +1308,10 @@ mod tests {
#[test]
fn test_invoke_context_compute_budget() {
let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
let transaction_accounts =
vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
invoke_context.compute_budget =
ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64);
@ -1404,22 +1337,11 @@ mod tests {
let dummy_account = AccountSharedData::new(10, 0, &program_key);
let mut program_account = AccountSharedData::new(500, 500, &native_loader::id());
program_account.set_executable(true);
let accounts = vec![
let transaction_accounts = vec![
(Pubkey::new_unique(), user_account),
(Pubkey::new_unique(), dummy_account),
(program_key, program_account),
];
let builtin_programs = [BuiltinProgram {
program_id: program_key,
process_instruction: mock_process_instruction,
}];
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
let mut invoke_context =
InvokeContext::new_mock(&mut transaction_context, &builtin_programs);
let instruction_accounts = [
InstructionAccount {
index_in_transaction: 0,
@ -1436,11 +1358,17 @@ mod tests {
is_writable: false,
},
];
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
let builtin_programs = &[BuiltinProgram {
program_id: program_key,
process_instruction: mock_process_instruction,
}];
invoke_context.builtin_programs = builtin_programs;
// Test: Resize the account to *the same size*, so not consuming any additional size; this must succeed
{
let resize_delta: i64 = 0;
let new_len = (user_account_data_len as i64 + resize_delta) as u64;
let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
let instruction_data =
bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
@ -1465,7 +1393,7 @@ mod tests {
// Test: Resize the account larger; this must succeed
{
let resize_delta: i64 = 1;
let new_len = (user_account_data_len as i64 + resize_delta) as u64;
let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
let instruction_data =
bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
@ -1490,7 +1418,7 @@ mod tests {
// Test: Resize the account smaller; this must succeed
{
let resize_delta: i64 = -1;
let new_len = (user_account_data_len as i64 + resize_delta) as u64;
let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
let instruction_data =
bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();

View File

@ -1725,10 +1725,9 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
super::process_instruction,
|_invoke_context| {},
)
}
@ -2011,12 +2010,10 @@ mod tests {
&[],
vec![(program_id, program_account.clone())],
Vec::new(),
None,
None,
Err(InstructionError::ProgramFailedToComplete),
|invoke_context: &mut InvokeContext| {
super::process_instruction,
|invoke_context| {
invoke_context.mock_set_remaining(0);
super::process_instruction(invoke_context)
},
);
@ -2557,10 +2554,9 @@ mod tests {
&instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
super::process_instruction,
|_invoke_context| {},
)
}

View File

@ -447,14 +447,14 @@ pub fn deserialize_parameters_aligned(
mod tests {
use {
super::*,
solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
solana_program_runtime::with_mock_invoke_context,
solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
account::{Account, AccountSharedData, WritableAccount},
account_info::AccountInfo,
bpf_loader,
entrypoint::deserialize,
instruction::AccountMeta,
sysvar::rent::Rent,
transaction_context::InstructionAccount,
},
std::{
cell::RefCell,
@ -537,13 +537,9 @@ mod tests {
rent_epoch: 0,
}),
)];
let instruction_account_keys: Vec<Pubkey> =
(0..num_ix_accounts).map(|_| Pubkey::new_unique()).collect();
for key in &instruction_account_keys {
for _ in 0..num_ix_accounts {
transaction_accounts.push((
*key,
Pubkey::new_unique(),
AccountSharedData::from(Account {
lamports: 0,
data: vec![],
@ -553,22 +549,19 @@ mod tests {
}),
));
}
let mut instruction_account_metas: Vec<_> = instruction_account_keys
.iter()
.map(|key| AccountMeta::new_readonly(*key, false))
let mut instruction_accounts: Vec<_> = (0..num_ix_accounts as IndexOfAccount)
.map(|index_in_callee| InstructionAccount {
index_in_transaction: index_in_callee + 1,
index_in_caller: index_in_callee + 1,
index_in_callee,
is_signer: false,
is_writable: false,
})
.collect();
if append_dup_account {
instruction_account_metas.push(instruction_account_metas.last().cloned().unwrap());
instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
}
let program_indices = [0];
let instruction_accounts = prepare_mock_invoke_context(
transaction_accounts.clone(),
instruction_account_metas,
&program_indices,
)
.instruction_accounts;
let instruction_data = vec![];
let mut transaction_context =
@ -693,41 +686,28 @@ mod tests {
}),
),
];
let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
let instruction_accounts: Vec<InstructionAccount> = [1, 1, 2, 3, 4, 4, 5, 6]
.into_iter()
.enumerate()
.map(
|(instruction_account_index, index_in_transaction)| AccountMeta {
pubkey: transaction_accounts.get(index_in_transaction).unwrap().0,
|(index_in_instruction, index_in_transaction)| InstructionAccount {
index_in_transaction,
index_in_caller: index_in_transaction,
index_in_callee: index_in_transaction - 1,
is_signer: false,
is_writable: instruction_account_index >= 4,
is_writable: index_in_instruction >= 4,
},
)
.collect();
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let program_indices = [0];
let mut original_accounts = transaction_accounts.clone();
let preparation = prepare_mock_invoke_context(
transaction_accounts,
instruction_accounts,
&program_indices,
);
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
Some(Rent::default()),
1,
1,
);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(
&program_indices,
&preparation.instruction_accounts,
&instruction_data,
);
.configure(&program_indices, &instruction_accounts, &instruction_data);
invoke_context.push().unwrap();
let instruction_context = invoke_context
.transaction_context

View File

@ -1123,6 +1123,7 @@ mod tests {
use {
super::*,
crate::allocator_bump::BpfAllocator,
solana_program_runtime::with_mock_invoke_context,
solana_rbpf::{
aligned_memory::AlignedMemory, ebpf::MM_INPUT_START, memory_region::MemoryRegion,
vm::Config,
@ -1131,8 +1132,7 @@ mod tests {
account::{Account, AccountSharedData},
clock::Epoch,
instruction::Instruction,
rent::Rent,
transaction_context::{TransactionAccount, TransactionContext},
transaction_context::TransactionAccount,
},
std::{
cell::{Cell, RefCell},
@ -1168,12 +1168,7 @@ mod tests {
.into_iter()
.map(|a| (a.0, a.1))
.collect::<Vec<TransactionAccount>>();
let program_accounts = program_accounts;
let mut $transaction_context =
TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
let mut $invoke_context = InvokeContext::new_mock(&mut $transaction_context, &[]);
with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
$invoke_context
.transaction_context
.get_next_instruction_context()

View File

@ -1812,7 +1812,7 @@ mod tests {
super::*,
crate::BpfAllocator,
core::slice,
solana_program_runtime::{invoke_context::InvokeContext, sysvar_cache::SysvarCache},
solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
solana_rbpf::{
aligned_memory::AlignedMemory,
ebpf::{self, HOST_ALIGN},
@ -1820,17 +1820,16 @@ mod tests {
vm::{BuiltInFunction, Config},
},
solana_sdk::{
account::AccountSharedData,
account::{create_account_shared_data_for_test, AccountSharedData},
bpf_loader,
fee_calculator::FeeCalculator,
hash::hashv,
instruction::Instruction,
program::check_type_assumptions,
stable_layout::stable_instruction::StableInstruction,
sysvar::{clock::Clock, epoch_schedule::EpochSchedule, rent::Rent},
transaction_context::TransactionContext,
sysvar::{self, clock::Clock, epoch_schedule::EpochSchedule},
},
std::{borrow::Cow, cell::RefCell, mem, rc::Rc, str::FromStr},
std::{mem, str::FromStr},
};
macro_rules! assert_access_violation {
@ -1847,7 +1846,6 @@ mod tests {
macro_rules! prepare_mockup {
($invoke_context:ident,
$transaction_context:ident,
$program_key:ident,
$loader_key:expr $(,)?) => {
let $program_key = Pubkey::new_unique();
@ -1858,9 +1856,7 @@ mod tests {
),
($program_key, AccountSharedData::new(0, 0, &$loader_key)),
];
let mut $transaction_context =
TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
let mut $invoke_context = InvokeContext::new_mock(&mut $transaction_context, &[]);
with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
$invoke_context
.transaction_context
.get_next_instruction_context()
@ -2062,12 +2058,7 @@ mod tests {
#[test]
#[should_panic(expected = "UserError(SyscallError(Abort))")]
fn test_syscall_abort() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(vec![], &config).unwrap();
let mut result = ProgramResult::Ok(0);
@ -2087,12 +2078,7 @@ mod tests {
#[test]
#[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")]
fn test_syscall_sol_panic() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let string = "Gaggablaghblagh!";
let config = Config::default();
@ -2138,12 +2124,7 @@ mod tests {
#[test]
fn test_syscall_sol_log() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let string = "Gaggablaghblagh!";
let config = Config::default();
@ -2221,12 +2202,7 @@ mod tests {
#[test]
fn test_syscall_sol_log_u64() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context.get_compute_budget().log_64_units;
invoke_context.mock_set_remaining(cost);
@ -2257,12 +2233,7 @@ mod tests {
#[test]
fn test_syscall_sol_pubkey() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context.get_compute_budget().log_pubkey_units;
let pubkey = Pubkey::from_str("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN").unwrap();
@ -2335,12 +2306,7 @@ mod tests {
// large alloc
{
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
let mut memory_mapping = MemoryMapping::new(
vec![
@ -2400,12 +2366,7 @@ mod tests {
// many small unaligned allocs
{
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
let mut memory_mapping = MemoryMapping::new(
vec![
@ -2455,12 +2416,7 @@ mod tests {
// many small aligned allocs
{
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
let mut memory_mapping = MemoryMapping::new(
vec![
@ -2511,12 +2467,7 @@ mod tests {
// aligned allocs
fn aligned<T>() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
let config = Config::default();
let mut memory_mapping = MemoryMapping::new(
@ -2565,12 +2516,7 @@ mod tests {
#[test]
fn test_syscall_sha256() {
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader_deprecated::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader_deprecated::id());
let bytes1 = "Gaggablaghblagh!";
let bytes2 = "flurbos";
@ -2685,12 +2631,7 @@ mod tests {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_EDWARDS;
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let valid_bytes: [u8; 32] = [
201, 179, 241, 122, 180, 185, 239, 50, 183, 52, 221, 0, 153, 195, 43, 18, 22, 38, 187,
@ -2770,12 +2711,7 @@ mod tests {
use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_RISTRETTO;
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let valid_bytes: [u8; 32] = [
226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11,
@ -2857,12 +2793,7 @@ mod tests {
};
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let left_point: [u8; 32] = [
33, 124, 71, 170, 117, 69, 151, 247, 59, 12, 95, 125, 133, 166, 64, 5, 2, 27, 90, 27,
@ -3034,12 +2965,7 @@ mod tests {
};
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let left_point: [u8; 32] = [
208, 165, 125, 204, 2, 100, 218, 17, 170, 194, 23, 9, 102, 156, 134, 136, 217, 190, 98,
@ -3213,12 +3139,7 @@ mod tests {
};
let config = Config::default();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let scalar_a: [u8; 32] = [
254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250,
@ -3378,13 +3299,25 @@ mod tests {
sysvar_cache.set_fees(src_fees.clone());
sysvar_cache.set_rent(src_rent);
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
invoke_context.sysvar_cache = Cow::Owned(sysvar_cache);
let transaction_accounts = vec![
(
sysvar::clock::id(),
create_account_shared_data_for_test(&src_clock),
),
(
sysvar::epoch_schedule::id(),
create_account_shared_data_for_test(&src_epochschedule),
),
(
sysvar::fees::id(),
create_account_shared_data_for_test(&src_fees),
),
(
sysvar::rent::id(),
create_account_shared_data_for_test(&src_rent),
),
];
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
// Test clock sysvar
{
@ -3635,12 +3568,7 @@ mod tests {
)
.unwrap();
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let mut result = ProgramResult::Ok(0);
SyscallSetReturnData::call(
@ -3700,13 +3628,20 @@ mod tests {
})
.collect::<Vec<_>>();
let instruction_trace = [1, 2, 3, 2, 2, 3, 4, 3];
let mut transaction_context =
TransactionContext::new(transaction_accounts, None, 4, instruction_trace.len());
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
while stack_height <= transaction_context.get_instruction_context_stack_height() {
transaction_context.pop().unwrap();
while stack_height
<= invoke_context
.transaction_context
.get_instruction_context_stack_height()
{
invoke_context.transaction_context.pop().unwrap();
}
if stack_height > transaction_context.get_instruction_context_stack_height() {
if stack_height
> invoke_context
.transaction_context
.get_instruction_context_stack_height()
{
let instruction_accounts = [InstructionAccount {
index_in_transaction: index_in_trace.saturating_add(1) as IndexOfAccount,
index_in_caller: 0, // This is incorrect / inconsistent but not required
@ -3714,14 +3649,14 @@ mod tests {
is_signer: false,
is_writable: false,
}];
transaction_context
invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(&[0], &instruction_accounts, &[index_in_trace as u8]);
transaction_context.push().unwrap();
invoke_context.transaction_context.push().unwrap();
}
}
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let syscall_base_cost = invoke_context.get_compute_budget().syscall_base_cost;
@ -3840,12 +3775,7 @@ mod tests {
fn test_create_program_address() {
// These tests duplicate the direct tests in solana_program::pubkey
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let address = bpf_loader_upgradeable::id();
let exceeded_seed = &[127; MAX_SEED_LEN + 1];
@ -3954,12 +3884,7 @@ mod tests {
#[test]
fn test_find_program_address() {
prepare_mockup!(
invoke_context,
transaction_context,
program_id,
bpf_loader::id(),
);
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
let cost = invoke_context
.get_compute_budget()
.create_program_address_units;

View File

@ -170,10 +170,9 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
super::process_instruction,
|_invoke_context| {},
)
}

View File

@ -642,10 +642,9 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
super::process_instruction,
|_invoke_context| {},
)
}

View File

@ -15,10 +15,7 @@ use {
create_ebpf_vm, create_vm, serialization::serialize_parameters, syscalls::create_loader,
},
solana_measure::measure::Measure,
solana_program_runtime::{
compute_budget::ComputeBudget,
invoke_context::{with_mock_invoke_context, InvokeContext},
},
solana_program_runtime::{compute_budget::ComputeBudget, invoke_context::InvokeContext},
solana_rbpf::{
ebpf::MM_INPUT_START,
elf::Executable,
@ -33,13 +30,17 @@ use {
loader_utils::{load_program, load_program_from_file},
},
solana_sdk::{
account::AccountSharedData,
bpf_loader,
client::SyncClient,
entrypoint::SUCCESS,
feature_set::FeatureSet,
instruction::{AccountMeta, Instruction},
message::Message,
native_loader,
pubkey::Pubkey,
signature::Signer,
transaction_context::InstructionAccount,
},
std::{mem, sync::Arc},
test::Bencher,
@ -48,6 +49,41 @@ use {
const ARMSTRONG_LIMIT: u64 = 500;
const ARMSTRONG_EXPECTED: u64 = 5;
macro_rules! with_mock_invoke_context {
($invoke_context:ident, $loader_id:expr, $account_size:expr) => {
let program_key = Pubkey::new_unique();
let transaction_accounts = vec![
(
$loader_id,
AccountSharedData::new(0, 0, &native_loader::id()),
),
(program_key, AccountSharedData::new(1, 0, &$loader_id)),
(
Pubkey::new_unique(),
AccountSharedData::new(2, $account_size, &program_key),
),
];
let instruction_accounts = vec![InstructionAccount {
index_in_transaction: 2,
index_in_caller: 2,
index_in_callee: 0,
is_signer: false,
is_writable: false,
}];
solana_program_runtime::with_mock_invoke_context!(
$invoke_context,
transaction_context,
transaction_accounts
);
$invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(&[0, 1], &instruction_accounts, &[]);
$invoke_context.push().unwrap();
};
}
#[bench]
fn bench_program_create_executable(bencher: &mut Bencher) {
let elf = load_program_from_file("bench_alu");
@ -75,82 +111,81 @@ fn bench_program_alu(bencher: &mut Bencher) {
.unwrap();
inner_iter.write_u64::<LittleEndian>(0).unwrap();
let elf = load_program_from_file("bench_alu");
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| {
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
verified_executable.jit_compile().unwrap();
create_vm!(
vm,
&verified_executable,
stack,
heap,
vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
vec![],
invoke_context
);
let mut vm = vm.unwrap();
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
println!("Interpreted:");
verified_executable.jit_compile().unwrap();
create_vm!(
vm,
&verified_executable,
stack,
heap,
vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
vec![],
&mut invoke_context
);
let mut vm = vm.unwrap();
println!("Interpreted:");
vm.env
.context_object_pointer
.mock_set_remaining(std::i64::MAX as u64);
let (instructions, result) = vm.execute_program(true);
assert_eq!(SUCCESS, result.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| {
vm.env
.context_object_pointer
.mock_set_remaining(std::i64::MAX as u64);
let (instructions, result) = vm.execute_program(true);
assert_eq!(SUCCESS, result.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| {
vm.env
.context_object_pointer
.mock_set_remaining(std::i64::MAX as u64);
vm.execute_program(true).1.unwrap();
});
let summary = bencher.bench(|_bencher| Ok(())).unwrap().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(false).1.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| {
vm.env
.context_object_pointer
.mock_set_remaining(std::i64::MAX as u64);
vm.execute_program(false).1.unwrap();
});
let summary = bencher.bench(|_bencher| Ok(())).unwrap().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);
vm.execute_program(true).1.unwrap();
});
let summary = bencher.bench(|_bencher| Ok(())).unwrap().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(false).1.unwrap());
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| {
vm.env
.context_object_pointer
.mock_set_remaining(std::i64::MAX as u64);
vm.execute_program(false).1.unwrap();
});
let summary = bencher.bench(|_bencher| Ok(())).unwrap().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]
@ -190,111 +225,107 @@ fn bench_program_execute_noop(bencher: &mut Bencher) {
#[bench]
fn bench_create_vm(bencher: &mut Bencher) {
let elf = load_program_from_file("noop");
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| {
const BUDGET: u64 = 200_000;
invoke_context.mock_set_remaining(BUDGET);
with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
const BUDGET: u64 = 200_000;
invoke_context.mock_set_remaining(BUDGET);
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // should_cap_ix_accounts
)
.unwrap();
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // should_cap_ix_accounts
)
.unwrap();
bencher.iter(|| {
create_vm!(
vm,
&verified_executable,
stack,
heap,
clone_regions(&regions),
account_lengths.clone(),
invoke_context
);
let _ = vm.unwrap();
});
bencher.iter(|| {
create_vm!(
vm,
&verified_executable,
stack,
heap,
clone_regions(&regions),
account_lengths.clone(),
&mut invoke_context
);
let _ = vm.unwrap();
});
}
#[bench]
fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
let elf = load_program_from_file("tuner");
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 10000001, true, |invoke_context| {
const BUDGET: u64 = 200_000;
invoke_context.mock_set_remaining(BUDGET);
with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
const BUDGET: u64 = 200_000;
invoke_context.mock_set_remaining(BUDGET);
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // should_cap_ix_accounts
)
.unwrap();
// Serialize account data
let (_serialized, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // should_cap_ix_accounts
)
.unwrap();
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
false,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
create_vm!(
vm,
&verified_executable,
stack,
heap,
regions,
account_lengths,
invoke_context
);
let mut vm = vm.unwrap();
create_vm!(
vm,
&verified_executable,
stack,
heap,
regions,
account_lengths,
&mut invoke_context
);
let mut vm = vm.unwrap();
let mut measure = Measure::start("tune");
let (instructions, _result) = vm.execute_program(true);
measure.stop();
let mut measure = Measure::start("tune");
let (instructions, _result) = vm.execute_program(true);
measure.stop();
assert_eq!(
0,
vm.env.context_object_pointer.get_remaining(),
"Tuner must consume the whole budget"
);
println!(
"{:?} compute units took {:?} us ({:?} instructions)",
BUDGET - vm.env.context_object_pointer.get_remaining(),
measure.as_us(),
instructions,
);
});
assert_eq!(
0,
vm.env.context_object_pointer.get_remaining(),
"Tuner must consume the whole budget"
);
println!(
"{:?} compute units took {:?} us ({:?} instructions)",
BUDGET - vm.env.context_object_pointer.get_remaining(),
measure.as_us(),
instructions,
);
}
fn clone_regions(regions: &[MemoryRegion]) -> Vec<MemoryRegion> {

View File

@ -22,6 +22,7 @@ use {
solana_program_runtime::{
compute_budget::ComputeBudget, invoke_context::InvokeContext, timings::ExecuteTimings,
},
solana_rbpf::vm::ContextObject,
solana_runtime::{
bank::{
DurableNonceFee, InnerInstruction, TransactionBalancesSet, TransactionExecutionDetails,
@ -60,13 +61,7 @@ use {
std::collections::HashMap,
};
use {
solana_bpf_loader_program::{
create_ebpf_vm, create_vm,
serialization::{deserialize_parameters, serialize_parameters},
syscalls::create_loader,
},
solana_program_runtime::invoke_context::with_mock_invoke_context,
solana_rbpf::{elf::Executable, verifier::RequisiteVerifier, vm::VerifiedExecutable},
solana_program_runtime::invoke_context::mock_process_instruction,
solana_runtime::{
bank::Bank,
bank_client::BankClient,
@ -80,7 +75,6 @@ use {
bpf_loader, bpf_loader_deprecated,
client::SyncClient,
clock::UnixTimestamp,
entrypoint::SUCCESS,
fee_calculator::FeeRateGovernor,
genesis_config::ClusterType,
hash::Hash,
@ -95,131 +89,6 @@ use {
std::{str::FromStr, sync::Arc, time::Duration},
};
fn run_program(name: &str) -> u64 {
let elf = load_program_from_file(name);
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 0, false, |invoke_context| {
let loader = create_loader(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
true,
true,
)
.unwrap();
let executable = Executable::<InvokeContext>::from_elf(&elf, loader).unwrap();
#[allow(unused_mut)]
let mut verified_executable =
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
.unwrap();
let run_program_iterations = {
#[cfg(target_arch = "x86_64")]
{
verified_executable.jit_compile().unwrap();
2
}
#[cfg(not(target_arch = "x86_64"))]
1
};
let mut instruction_count = 0;
let mut trace_log = None;
for i in 0..run_program_iterations {
let transaction_context = &mut invoke_context.transaction_context;
let instruction_context = transaction_context
.get_current_instruction_context()
.unwrap();
let caller = *instruction_context
.get_last_program_key(transaction_context)
.unwrap();
transaction_context
.set_return_data(caller, Vec::new())
.unwrap();
let (parameter_bytes, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // should_cap_ix_accounts
)
.unwrap();
{
create_vm!(
vm,
&verified_executable,
stack,
heap,
regions,
account_lengths.clone(),
invoke_context
);
let mut vm = vm.unwrap();
let (compute_units_consumed, result) = vm.execute_program(i == 0);
assert_eq!(SUCCESS, result.unwrap());
if i == 1 {
assert_eq!(instruction_count, compute_units_consumed);
}
instruction_count = compute_units_consumed;
if i == 0 {
trace_log = Some(
vm.env
.context_object_pointer
.trace_log_stack
.last()
.expect("Inconsistent trace log stack")
.trace_log
.clone(),
);
} else {
let interpreter = trace_log.as_ref().unwrap().as_slice();
let mut jit = vm
.env
.context_object_pointer
.trace_log_stack
.last()
.expect("Inconsistent trace log stack")
.trace_log
.as_slice();
if jit.len() > interpreter.len() {
jit = &jit[0..interpreter.len()];
}
assert_eq!(interpreter, jit);
trace_log = None;
}
}
assert!(match deserialize_parameters(
invoke_context.transaction_context,
invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap(),
parameter_bytes.as_slice(),
&account_lengths,
) {
Ok(()) => true,
Err(InstructionError::ModifiedProgramId) => true,
Err(InstructionError::ExternalAccountLamportSpend) => true,
Err(InstructionError::ReadonlyLamportChange) => true,
Err(InstructionError::ExecutableLamportChange) => true,
Err(InstructionError::ExecutableAccountNotRentExempt) => true,
Err(InstructionError::ExecutableModified) => true,
Err(InstructionError::AccountDataSizeChanged) => true,
Err(InstructionError::InvalidRealloc) => true,
Err(InstructionError::ExecutableDataModified) => true,
Err(InstructionError::ReadonlyDataModified) => true,
Err(InstructionError::ExternalAccountDataModified) => true,
_ => false,
});
}
instruction_count
})
}
#[cfg(feature = "sbf_rust")]
fn process_transaction_and_record_inner(
bank: &Bank,
@ -1503,8 +1372,8 @@ fn assert_instruction_count() {
("noop++", 5),
("relative_call", 210),
("return_data", 980),
("sanity", 2377),
("sanity++", 2277),
("sanity", 3259),
("sanity++", 3159),
("secp256k1_recover", 25383),
("sha", 1355),
("struct_pass", 108),
@ -1526,30 +1395,70 @@ fn assert_instruction_count() {
("solana_sbf_rust_noop", 275),
("solana_sbf_rust_param_passing", 146),
("solana_sbf_rust_rand", 378),
("solana_sbf_rust_sanity", 51931),
("solana_sbf_rust_sanity", 10759),
("solana_sbf_rust_secp256k1_recover", 91185),
("solana_sbf_rust_sha", 24059),
]);
}
let mut passed = true;
println!("\n {:36} expected actual diff", "SBF program");
for program in programs.iter() {
let count = run_program(program.0);
let diff: i64 = count as i64 - program.1 as i64;
println!(
" {:36} {:8} {:6} {:+5} ({:+3.0}%)",
program.0,
program.1,
count,
diff,
100.0_f64 * count as f64 / program.1 as f64 - 100.0_f64,
for (program_name, expected_consumption) in programs.iter() {
let loader_id = bpf_loader::id();
let program_key = Pubkey::new_unique();
let mut transaction_accounts = vec![
(program_key, AccountSharedData::new(0, 0, &loader_id)),
(
Pubkey::new_unique(),
AccountSharedData::new(0, 8, &program_key),
),
];
let instruction_accounts = vec![AccountMeta {
pubkey: transaction_accounts[1].0,
is_signer: false,
is_writable: false,
}];
transaction_accounts[0]
.1
.set_data_from_slice(&load_program_from_file(program_name));
transaction_accounts[0].1.set_executable(true);
transaction_accounts[1]
.1
.set_state(expected_consumption)
.unwrap();
print!(" {:36} {:8}", program_name, *expected_consumption);
mock_process_instruction(
&loader_id,
vec![0],
&[],
transaction_accounts,
instruction_accounts,
Ok(()),
|invoke_context: &mut InvokeContext| {
let expected_consumption: u64 = invoke_context
.transaction_context
.get_current_instruction_context()
.unwrap()
.try_borrow_instruction_account(&invoke_context.transaction_context, 0)
.unwrap()
.get_state()
.unwrap();
let prev_compute_meter = invoke_context.get_remaining();
let _result = solana_bpf_loader_program::process_instruction(invoke_context);
let consumption = prev_compute_meter.saturating_sub(invoke_context.get_remaining());
let diff: i64 = consumption as i64 - expected_consumption as i64;
println!(
"{:6} {:+5} ({:+3.0}%)",
consumption,
diff,
100.0_f64 * consumption as f64 / expected_consumption as f64 - 100.0_f64,
);
assert_eq!(consumption, expected_consumption);
Ok(())
},
|_invoke_context| {},
);
if count > program.1 {
passed = false;
}
}
assert!(passed);
}
#[test]

File diff suppressed because it is too large Load Diff

View File

@ -1779,13 +1779,12 @@ mod tests {
use {
super::*,
proptest::prelude::*,
solana_program_runtime::invoke_context::InvokeContext,
solana_program_runtime::with_mock_invoke_context,
solana_sdk::{
account::{create_account_shared_data_for_test, AccountSharedData},
native_token,
pubkey::Pubkey,
sysvar::SysvarId,
transaction_context::TransactionContext,
},
};
@ -2916,18 +2915,6 @@ mod tests {
);
}
fn create_mock_tx_context() -> TransactionContext {
TransactionContext::new(
vec![(
Rent::id(),
create_account_shared_data_for_test(&Rent::default()),
)],
Some(Rent::default()),
1,
1,
)
}
#[test]
fn test_lockup_is_expired() {
let custodian = solana_sdk::pubkey::new_rand();
@ -3034,9 +3021,7 @@ mod tests {
#[test]
fn test_things_can_merge() {
let mut transaction_context =
TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, Vec::new());
let good_stake = Stake {
credits_observed: 4242,
delegation: Delegation {
@ -3134,9 +3119,7 @@ mod tests {
#[test]
fn test_metas_can_merge() {
let mut transaction_context =
TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, Vec::new());
// Identical Metas can merge
assert!(MergeKind::metas_can_merge(
&invoke_context,
@ -3282,9 +3265,7 @@ mod tests {
#[test]
fn test_merge_kind_get_if_mergeable() {
let mut transaction_context =
TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, Vec::new());
let authority_pubkey = Pubkey::new_unique();
let initial_lamports = 4242424242;
let rent = Rent::default();
@ -3522,9 +3503,7 @@ mod tests {
#[test]
fn test_merge_kind_merge() {
let mut transaction_context =
TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, Vec::new());
let clock = Clock::default();
let lamports = 424242;
let meta = Meta {
@ -3603,8 +3582,11 @@ mod tests {
#[test]
fn test_active_stake_merge() {
let mut transaction_context = create_mock_tx_context();
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let transaction_accounts = vec![(
Rent::id(),
create_account_shared_data_for_test(&Rent::default()),
)];
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
let clock = Clock::default();
let delegation_a = 4_242_424_242u64;
let delegation_b = 6_200_000_000u64;

View File

@ -3,7 +3,7 @@
extern crate test;
use {
solana_program_runtime::invoke_context::InvokeContext,
solana_program_runtime::with_mock_invoke_context,
solana_sdk::{
account::{create_account_for_test, Account, AccountSharedData},
clock::{Clock, Slot},
@ -11,9 +11,7 @@ use {
pubkey::Pubkey,
slot_hashes::{SlotHashes, MAX_ENTRIES},
sysvar,
transaction_context::{
IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
},
transaction_context::{IndexOfAccount, InstructionAccount, TransactionAccount},
},
solana_vote_program::{
vote_instruction::VoteInstruction,
@ -109,13 +107,8 @@ fn bench_process_vote_instruction(
instruction_data: Vec<u8>,
) {
bencher.iter(|| {
let mut transaction_context = TransactionContext::new(
transaction_accounts.clone(),
Some(sysvar::rent::Rent::default()),
1,
1,
);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
let transaction_accounts = transaction_accounts.clone();
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
invoke_context
.transaction_context
.get_next_instruction_context()

View File

@ -326,10 +326,9 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
super::process_instruction,
|_invoke_context| {},
)
}
@ -345,10 +344,11 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
Some(std::sync::Arc::new(FeatureSet::default())),
expected_result,
super::process_instruction,
|invoke_context| {
invoke_context.feature_set = std::sync::Arc::new(FeatureSet::default());
},
)
}

View File

@ -5,17 +5,16 @@ use {
solana_bpf_loader_program::{
create_ebpf_vm, create_vm, serialization::serialize_parameters, syscalls::create_loader,
},
solana_program_runtime::{
compute_budget::ComputeBudget,
invoke_context::{prepare_mock_invoke_context, InvokeContext},
},
solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
solana_rbpf::{
assembler::assemble, elf::Executable, static_analysis::Analysis,
verifier::RequisiteVerifier, vm::VerifiedExecutable,
},
solana_sdk::{
account::AccountSharedData, bpf_loader, instruction::AccountMeta, pubkey::Pubkey,
sysvar::rent::Rent, transaction_context::TransactionContext,
account::AccountSharedData,
bpf_loader,
pubkey::Pubkey,
transaction_context::{IndexOfAccount, InstructionAccount},
},
std::{
fmt::{Debug, Formatter},
@ -186,8 +185,10 @@ before execting it in the virtual machine.",
pubkey,
AccountSharedData::new(0, allocation_size, &Pubkey::new_unique()),
));
instruction_accounts.push(AccountMeta {
pubkey,
instruction_accounts.push(InstructionAccount {
index_in_transaction: 0,
index_in_caller: 0,
index_in_callee: 0,
is_signer: false,
is_writable: true,
});
@ -195,43 +196,32 @@ before execting it in the virtual machine.",
}
Err(_) => {
let input = load_accounts(Path::new(matches.value_of("input").unwrap())).unwrap();
for account_info in input.accounts {
for (index, account_info) in input.accounts.into_iter().enumerate() {
let mut account = AccountSharedData::new(
account_info.lamports,
account_info.data.len(),
&account_info.owner,
);
account.set_data(account_info.data);
instruction_accounts.push(AccountMeta {
pubkey: account_info.key,
transaction_accounts.push((account_info.key, account));
instruction_accounts.push(InstructionAccount {
index_in_transaction: index as IndexOfAccount,
index_in_caller: index as IndexOfAccount,
index_in_callee: index as IndexOfAccount,
is_signer: account_info.is_signer,
is_writable: account_info.is_writable,
});
transaction_accounts.push((account_info.key, account));
}
input.instruction_data
}
};
let program_indices = [0, 1];
let preparation =
prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
let mut transaction_context = TransactionContext::new(
preparation.transaction_accounts,
Some(Rent::default()),
1,
1,
);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
invoke_context.enable_instruction_tracing = true;
invoke_context
.transaction_context
.get_next_instruction_context()
.unwrap()
.configure(
&program_indices,
&preparation.instruction_accounts,
&instruction_data,
);
.configure(&[0, 1], &instruction_accounts, &instruction_data);
invoke_context.push().unwrap();
let (_parameter_bytes, regions, account_lengths) = serialize_parameters(
invoke_context.transaction_context,

View File

@ -8050,10 +8050,9 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
(program_keypair.pubkey(), post_program_account),
],
Vec::new(),
None,
None,
Ok(()),
solana_bpf_loader_program::process_instruction,
|_invoke_context| {},
);
// Test initialized program account

View File

@ -21,7 +21,7 @@ use {
transaction::TransactionError,
transaction_context::{IndexOfAccount, InstructionAccount, TransactionContext},
},
std::{borrow::Cow, cell::RefCell, rc::Rc, sync::Arc},
std::{cell::RefCell, rc::Rc, sync::Arc},
};
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
@ -71,7 +71,7 @@ impl MessageProcessor {
transaction_context,
rent,
builtin_programs,
Cow::Borrowed(sysvar_cache),
sysvar_cache,
log_collector,
compute_budget,
tx_executor_cache,

View File

@ -272,15 +272,15 @@ mod test {
use {
super::*,
assert_matches::assert_matches,
solana_program_runtime::invoke_context::InvokeContext,
solana_program_runtime::with_mock_invoke_context,
solana_sdk::{
account::AccountSharedData,
hash::{hash, Hash},
hash::hash,
nonce::{self, State},
nonce_account::{create_account, verify_nonce_account},
system_instruction::SystemError,
system_program,
transaction_context::{InstructionAccount, TransactionContext},
transaction_context::InstructionAccount,
},
};
@ -309,7 +309,7 @@ mod test {
..Rent::default()
};
let from_lamports = $rent.minimum_balance(State::size()) + 42;
let accounts = vec![
let transaction_accounts = vec![
(
Pubkey::new_unique(),
create_account(from_lamports).into_inner(),
@ -333,9 +333,7 @@ mod test {
is_writable: true,
},
];
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 1);
let mut $invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
};
}

View File

@ -612,14 +612,14 @@ mod tests {
system_instruction, system_program,
sysvar::{self, recent_blockhashes::IterItem, rent::Rent},
transaction::TransactionError,
transaction_context::TransactionContext,
};
use {
super::*,
crate::{bank::Bank, bank_client::BankClient},
bincode::serialize,
solana_program_runtime::invoke_context::{
mock_process_instruction, InvokeContext, ProcessInstructionWithContext,
solana_program_runtime::{
invoke_context::{mock_process_instruction, ProcessInstructionWithContext},
with_mock_invoke_context,
},
std::sync::Arc,
};
@ -646,10 +646,9 @@ mod tests {
instruction_data,
transaction_accounts,
instruction_accounts,
None,
None,
expected_result,
process_instruction,
|_invoke_context| {},
)
}
@ -793,9 +792,7 @@ mod tests {
#[test]
fn test_address_create_with_seed_mismatch() {
let mut transaction_context =
TransactionContext::new(Vec::new(), Some(Rent::default()), 1, 1);
let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
with_mock_invoke_context!(invoke_context, transaction_context, Vec::new());
let from = Pubkey::new_unique();
let seed = "dull boy";
let to = Pubkey::new_unique();