Feature: TransactionContext, InstructionContext and BorrowedAccount (#21706)

* Adds TransactionContext, InstructionContext and BorrowedAccount.

* Redirects the usage of accounts in InvokeContext through TransactionContext.
Also use the types declared in transaction_context.rs everywhere.

* Adjusts all affected tests.
This commit is contained in:
Alexander Meißner 2021-12-27 18:49:32 +01:00 committed by GitHub
parent bb97c8fdcd
commit a06646631c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 926 additions and 530 deletions

View File

@ -42,6 +42,7 @@ use {
system_instruction::{self, SystemError},
system_program,
transaction::{Transaction, TransactionError},
transaction_context::TransactionContext,
},
std::{
fs::File,
@ -1990,7 +1991,8 @@ 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 invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Verify the program
Executable::<BpfError, ThisInstructionMeter>::from_elf(

View File

@ -20,20 +20,11 @@ use {
pubkey::Pubkey,
rent::Rent,
sysvar::Sysvar,
transaction_context::{InstructionAccount, TransactionAccount, TransactionContext},
},
std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc},
};
pub type TransactionAccountRefCell = (Pubkey, RefCell<AccountSharedData>);
pub type TransactionAccountRefCells = Vec<TransactionAccountRefCell>;
#[derive(Clone, Debug)]
pub struct InstructionAccount {
pub index: usize,
pub is_signer: bool,
pub is_writable: bool,
}
pub type ProcessInstructionWithContext =
fn(usize, &[u8], &mut InvokeContext) -> Result<(), InstructionError>;
@ -144,10 +135,11 @@ impl<'a> StackFrame<'a> {
}
pub struct InvokeContext<'a> {
pub transaction_context: &'a TransactionContext,
pub return_data: (Pubkey, Vec<u8>),
invoke_stack: Vec<StackFrame<'a>>,
rent: Rent,
pre_accounts: Vec<PreAccount>,
accounts: &'a [TransactionAccountRefCell],
builtin_programs: &'a [BuiltinProgram],
pub sysvars: &'a [(Pubkey, Vec<u8>)],
log_collector: Option<Rc<RefCell<LogCollector>>>,
@ -160,14 +152,13 @@ pub struct InvokeContext<'a> {
pub timings: ExecuteDetailsTimings,
pub blockhash: Hash,
pub lamports_per_signature: u64,
pub return_data: (Pubkey, Vec<u8>),
}
impl<'a> InvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
transaction_context: &'a TransactionContext,
rent: Rent,
accounts: &'a [TransactionAccountRefCell],
builtin_programs: &'a [BuiltinProgram],
sysvars: &'a [(Pubkey, Vec<u8>)],
log_collector: Option<Rc<RefCell<LogCollector>>>,
@ -179,10 +170,11 @@ impl<'a> InvokeContext<'a> {
lamports_per_signature: u64,
) -> Self {
Self {
transaction_context,
return_data: (Pubkey::default(), Vec::new()),
invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
rent,
pre_accounts: Vec::new(),
accounts,
builtin_programs,
sysvars,
log_collector,
@ -195,17 +187,16 @@ impl<'a> InvokeContext<'a> {
timings: ExecuteDetailsTimings::default(),
blockhash,
lamports_per_signature,
return_data: (Pubkey::default(), Vec::new()),
}
}
pub fn new_mock(
accounts: &'a [TransactionAccountRefCell],
transaction_context: &'a TransactionContext,
builtin_programs: &'a [BuiltinProgram],
) -> Self {
Self::new(
transaction_context,
Rent::default(),
accounts,
builtin_programs,
&[],
Some(LogCollector::new_ref()),
@ -228,9 +219,10 @@ impl<'a> InvokeContext<'a> {
return Err(InstructionError::CallDepth);
}
let program_id = program_indices
.last()
.map(|index_of_program_id| &self.accounts[*index_of_program_id].0);
let program_id = program_indices.last().map(|account_index| {
self.transaction_context
.get_key_of_account_at_index(*account_index)
});
if program_id.is_none()
&& self
.feature_set
@ -262,10 +254,17 @@ impl<'a> InvokeContext<'a> {
self.pre_accounts = Vec::with_capacity(instruction_accounts.len());
let mut work = |_index_in_instruction: usize, entry: &InstructionAccount| {
if entry.index < self.accounts.len() {
let account = self.accounts[entry.index].1.borrow().clone();
self.pre_accounts
.push(PreAccount::new(&self.accounts[entry.index].0, account));
if entry.index < self.transaction_context.get_number_of_accounts() {
let account = self
.transaction_context
.get_account_at_index(entry.index)
.borrow()
.clone();
self.pre_accounts.push(PreAccount::new(
self.transaction_context
.get_key_of_account_at_index(entry.index),
account,
));
return Ok(());
}
Err(InstructionError::MissingAccount)
@ -294,16 +293,20 @@ impl<'a> InvokeContext<'a> {
(
false,
false,
&self.accounts[*account_index].0,
&self.accounts[*account_index].1 as &RefCell<AccountSharedData>,
self.transaction_context
.get_key_of_account_at_index(*account_index),
self.transaction_context
.get_account_at_index(*account_index),
)
})
.chain(instruction_accounts.iter().map(|instruction_account| {
(
instruction_account.is_signer,
instruction_account.is_writable,
&self.accounts[instruction_account.index].0,
&self.accounts[instruction_account.index].1 as &RefCell<AccountSharedData>,
self.transaction_context
.get_key_of_account_at_index(instruction_account.index),
self.transaction_context
.get_account_at_index(instruction_account.index),
)
}))
.collect::<Vec<_>>();
@ -340,8 +343,8 @@ impl<'a> InvokeContext<'a> {
// Verify all executable accounts have zero outstanding refs
for account_index in program_indices.iter() {
self.accounts[*account_index]
.1
self.transaction_context
.get_account_at_index(*account_index)
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
}
@ -352,14 +355,18 @@ impl<'a> InvokeContext<'a> {
let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| {
{
// Verify account has no outstanding references
let _ = self.accounts[instruction_account.index]
.1
let _ = self
.transaction_context
.get_account_at_index(instruction_account.index)
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
}
let pre_account = &self.pre_accounts[pre_account_index];
pre_account_index = pre_account_index.saturating_add(1);
let account = self.accounts[instruction_account.index].1.borrow();
let account = self
.transaction_context
.get_account_at_index(instruction_account.index)
.borrow();
pre_account
.verify(
program_id,
@ -410,15 +417,17 @@ impl<'a> InvokeContext<'a> {
.ok_or(InstructionError::CallDepth)?;
let rent = &self.rent;
let log_collector = &self.log_collector;
let accounts = &self.accounts;
let transaction_context = self.transaction_context;
let pre_accounts = &mut self.pre_accounts;
let timings = &mut self.timings;
// Verify the per-account instruction results
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
let mut work = |index_in_instruction: usize, instruction_account: &InstructionAccount| {
if instruction_account.index < accounts.len() {
let (key, account) = &accounts[instruction_account.index];
if instruction_account.index < transaction_context.get_number_of_accounts() {
let key =
transaction_context.get_key_of_account_at_index(instruction_account.index);
let account = transaction_context.get_account_at_index(instruction_account.index);
let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
caller_write_privileges[index_in_instruction]
} else {
@ -487,8 +496,9 @@ impl<'a> InvokeContext<'a> {
self.prepare_instruction(&instruction, signers)?;
let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len());
for instruction_account in instruction_accounts.iter() {
let account_length = self.accounts[instruction_account.index]
.1
let account_length = self
.transaction_context
.get_account_at_index(instruction_account.index)
.borrow()
.data()
.len();
@ -506,7 +516,13 @@ impl<'a> InvokeContext<'a> {
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
for (account_index, prev_size) in prev_account_sizes.into_iter() {
if !do_support_realloc
&& prev_size != self.accounts[account_index].1.borrow().data().len()
&& prev_size
!= self
.transaction_context
.get_account_at_index(account_index)
.borrow()
.data()
.len()
&& prev_size != 0
{
// Only support for `CreateAccount` at this time.
@ -536,9 +552,8 @@ impl<'a> InvokeContext<'a> {
let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len());
for account_meta in instruction.accounts.iter() {
let account_index = self
.accounts
.iter()
.position(|(key, _account)| key == &account_meta.pubkey)
.transaction_context
.find_index_of_account(&account_meta.pubkey)
.ok_or_else(|| {
ic_msg!(
self,
@ -617,15 +632,17 @@ impl<'a> InvokeContext<'a> {
.iter()
.find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
.and_then(|_keyed_account| {
self.accounts
.iter()
.rposition(|(key, _account)| key == &callee_program_id)
self.transaction_context
.find_index_of_program_account(&callee_program_id)
})
.ok_or_else(|| {
ic_msg!(self, "Unknown program {}", callee_program_id);
InstructionError::MissingAccount
})?;
let program_account = self.accounts[program_account_index].1.borrow();
let program_account = self
.transaction_context
.get_account_at_index(program_account_index)
.borrow();
if !program_account.executable() {
ic_msg!(self, "Account {} is not executable", callee_program_id);
return Err(InstructionError::AccountNotExecutable);
@ -637,9 +654,8 @@ impl<'a> InvokeContext<'a> {
} = program_account.state()?
{
if let Some(programdata_account_index) = self
.accounts
.iter()
.rposition(|(key, _account)| key == &programdata_address)
.transaction_context
.find_index_of_program_account(&programdata_address)
{
program_indices.push(programdata_account_index);
} else {
@ -678,7 +694,7 @@ impl<'a> InvokeContext<'a> {
) -> Result<u64, InstructionError> {
let program_id = program_indices
.last()
.map(|index| self.accounts[*index].0)
.map(|index| *self.transaction_context.get_key_of_account_at_index(*index))
.unwrap_or_else(native_loader::id);
let is_lowest_invocation_level = self.invoke_stack.is_empty();
@ -694,9 +710,8 @@ impl<'a> InvokeContext<'a> {
if let Some(instruction_recorder) = &self.instruction_recorder {
let compiled_instruction = CompiledInstruction {
program_id_index: self
.accounts
.iter()
.position(|(key, _account)| *key == program_id)
.transaction_context
.find_index_of_account(&program_id)
.unwrap_or(0) as u8,
data: instruction_data.to_vec(),
accounts: instruction_accounts
@ -852,16 +867,6 @@ impl<'a> InvokeContext<'a> {
self.executors.borrow().get(pubkey)
}
/// Returns an account by its account_index
pub fn get_account_key_at_index(&self, account_index: usize) -> &Pubkey {
&self.accounts[account_index].0
}
/// Returns an account by its account_index
pub fn get_account_at_index(&self, account_index: usize) -> &RefCell<AccountSharedData> {
&self.accounts[account_index].1
}
/// Get this invocation's compute budget
pub fn get_compute_budget(&self) -> &ComputeBudget {
&self.current_compute_budget
@ -886,18 +891,14 @@ impl<'a> InvokeContext<'a> {
}
pub struct MockInvokeContextPreparation {
pub transaction_accounts: TransactionAccountRefCells,
pub transaction_accounts: Vec<TransactionAccount>,
pub instruction_accounts: Vec<InstructionAccount>,
}
pub fn prepare_mock_invoke_context(
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
) -> MockInvokeContextPreparation {
let transaction_accounts: TransactionAccountRefCells = transaction_accounts
.into_iter()
.map(|(pubkey, account)| (pubkey, RefCell::new(account)))
.collect();
let instruction_accounts = instruction_accounts
.iter()
.map(|account_meta| InstructionAccount {
@ -941,7 +942,11 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
is_writable: false,
}];
let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts);
let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]);
let transaction_context = TransactionContext::new(
preparation.transaction_accounts,
ComputeBudget::default().max_invoke_depth,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.unwrap();
@ -952,41 +957,38 @@ pub fn mock_process_instruction_with_sysvars(
loader_id: &Pubkey,
mut program_indices: Vec<usize>,
instruction_data: &[u8],
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
expected_result: Result<(), InstructionError>,
sysvars: &[(Pubkey, Vec<u8>)],
process_instruction: ProcessInstructionWithContext,
) -> Vec<AccountSharedData> {
let mut preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts);
let processor_account = RefCell::new(AccountSharedData::new(
0,
0,
&solana_sdk::native_loader::id(),
));
let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id());
program_indices.insert(0, preparation.transaction_accounts.len());
preparation
.transaction_accounts
.push((*loader_id, processor_account));
let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]);
let transaction_context = TransactionContext::new(
preparation.transaction_accounts,
ComputeBudget::default().max_invoke_depth,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.sysvars = sysvars;
let result = invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.and_then(|_| process_instruction(1, instruction_data, &mut invoke_context));
preparation.transaction_accounts.pop();
assert_eq!(result, expected_result);
preparation
.transaction_accounts
.into_iter()
.map(|(_key, account)| account.into_inner())
.collect()
let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
transaction_accounts.pop();
transaction_accounts
}
pub fn mock_process_instruction(
loader_id: &Pubkey,
program_indices: Vec<usize>,
instruction_data: &[u8],
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
expected_result: Result<(), InstructionError>,
process_instruction: ProcessInstructionWithContext,
@ -1166,11 +1168,7 @@ mod tests {
invoke_stack.push(solana_sdk::pubkey::new_rand());
accounts.push((
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(
index as u64,
1,
&invoke_stack[index],
)),
AccountSharedData::new(index as u64, 1, &invoke_stack[index]),
));
instruction_accounts.push(InstructionAccount {
index,
@ -1181,11 +1179,7 @@ mod tests {
for (index, program_id) in invoke_stack.iter().enumerate() {
accounts.push((
*program_id,
RefCell::new(AccountSharedData::new(
1,
1,
&solana_sdk::pubkey::Pubkey::default(),
)),
AccountSharedData::new(1, 1, &solana_sdk::pubkey::Pubkey::default()),
));
instruction_accounts.push(InstructionAccount {
index,
@ -1193,7 +1187,8 @@ mod tests {
is_writable: false,
});
}
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Check call depth increases and has a limit
let mut depth_reached = 0;
@ -1225,8 +1220,10 @@ mod tests {
];
// modify account owned by the program
accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] =
(MAX_DEPTH + owned_index) as u8;
transaction_context
.get_account_at_index(owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = (MAX_DEPTH + owned_index) as u8;
invoke_context
.verify_and_update(&instruction_accounts, None)
.unwrap();
@ -1236,15 +1233,23 @@ mod tests {
);
// modify account not owned by the program
let data = accounts[not_owned_index].1.borrow_mut().data()[0];
accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] =
(MAX_DEPTH + not_owned_index) as u8;
let data = transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data()[0];
transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = (MAX_DEPTH + not_owned_index) as u8;
assert_eq!(
invoke_context.verify_and_update(&instruction_accounts, None),
Err(InstructionError::ExternalAccountDataModified)
);
assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data);
accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = data;
transaction_context
.get_account_at_index(not_owned_index)
.borrow_mut()
.data_as_mut_slice()[0] = data;
invoke_context.pop();
}
@ -1252,13 +1257,11 @@ mod tests {
#[test]
fn test_invoke_context_verify() {
let accounts = vec![(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::default()),
)];
let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
let instruction_accounts = vec![];
let program_indices = vec![0];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context
.push(&instruction_accounts, &program_indices)
.unwrap();
@ -1266,7 +1269,7 @@ mod tests {
.verify(&instruction_accounts, &program_indices)
.is_ok());
let mut _borrowed = accounts[0].1.borrow();
let mut _borrowed = transaction_context.get_account_at_index(0).borrow();
assert_eq!(
invoke_context.verify(&instruction_accounts, &program_indices),
Err(InstructionError::AccountBorrowOutstanding)
@ -1277,6 +1280,10 @@ mod tests {
fn test_process_cross_program() {
let caller_program_id = solana_sdk::pubkey::new_rand();
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());
@ -1286,17 +1293,11 @@ mod tests {
program_account.set_executable(true);
let accounts = vec![
(solana_sdk::pubkey::new_rand(), RefCell::new(owned_account)),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(not_owned_account),
),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(readonly_account),
),
(caller_program_id, RefCell::new(loader_account)),
(callee_program_id, RefCell::new(program_account)),
(solana_sdk::pubkey::new_rand(), owned_account),
(solana_sdk::pubkey::new_rand(), not_owned_account),
(solana_sdk::pubkey::new_rand(), readonly_account),
(caller_program_id, loader_account),
(callee_program_id, program_account),
];
let program_indices = [3, 4];
@ -1319,17 +1320,17 @@ mod tests {
&MockInstruction::NoopSuccess,
metas.clone(),
);
let builtin_programs = &[BuiltinProgram {
program_id: callee_program_id,
process_instruction: mock_process_instruction,
}];
let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs);
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs);
invoke_context
.push(&instruction_accounts, &program_indices[..1])
.unwrap();
// not owned account modified by the caller (before the invoke)
accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1;
transaction_context
.get_account_at_index(1)
.borrow_mut()
.data_as_mut_slice()[0] = 1;
assert_eq!(
invoke_context.process_instruction(
&instruction.data,
@ -1339,10 +1340,16 @@ mod tests {
),
Err(InstructionError::ExternalAccountDataModified)
);
accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0;
transaction_context
.get_account_at_index(1)
.borrow_mut()
.data_as_mut_slice()[0] = 0;
// readonly account modified by the invoker
accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1;
transaction_context
.get_account_at_index(2)
.borrow_mut()
.data_as_mut_slice()[0] = 1;
assert_eq!(
invoke_context.process_instruction(
&instruction.data,
@ -1352,7 +1359,10 @@ mod tests {
),
Err(InstructionError::ReadonlyDataModified)
);
accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0;
transaction_context
.get_account_at_index(2)
.borrow_mut()
.data_as_mut_slice()[0] = 0;
invoke_context.pop();
@ -1401,16 +1411,10 @@ mod tests {
let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
program_account.set_executable(true);
let accounts = vec![
(solana_sdk::pubkey::new_rand(), RefCell::new(owned_account)),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(not_owned_account),
),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(readonly_account),
),
(callee_program_id, RefCell::new(program_account)),
(solana_sdk::pubkey::new_rand(), owned_account),
(solana_sdk::pubkey::new_rand(), not_owned_account),
(solana_sdk::pubkey::new_rand(), readonly_account),
(callee_program_id, program_account),
];
let program_indices = [3];
@ -1435,26 +1439,39 @@ mod tests {
metas.clone(),
);
let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs);
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs);
invoke_context
.push(&instruction_accounts, &program_indices)
.unwrap();
// not owned account modified by the invoker
accounts[1].1.borrow_mut().data_as_mut_slice()[0] = 1;
transaction_context
.get_account_at_index(1)
.borrow_mut()
.data_as_mut_slice()[0] = 1;
assert_eq!(
invoke_context.native_invoke(callee_instruction.clone(), &[]),
Err(InstructionError::ExternalAccountDataModified)
);
accounts[1].1.borrow_mut().data_as_mut_slice()[0] = 0;
transaction_context
.get_account_at_index(1)
.borrow_mut()
.data_as_mut_slice()[0] = 0;
// readonly account modified by the invoker
accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1;
transaction_context
.get_account_at_index(2)
.borrow_mut()
.data_as_mut_slice()[0] = 1;
assert_eq!(
invoke_context.native_invoke(callee_instruction, &[]),
Err(InstructionError::ReadonlyDataModified)
);
accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0;
transaction_context
.get_account_at_index(2)
.borrow_mut()
.data_as_mut_slice()[0] = 0;
invoke_context.pop();
@ -1492,20 +1509,15 @@ mod tests {
#[test]
fn test_invoke_context_compute_budget() {
let accounts = vec![
(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::default()),
),
(
crate::neon_evm_program::id(),
RefCell::new(AccountSharedData::default()),
),
(solana_sdk::pubkey::new_rand(), AccountSharedData::default()),
(crate::neon_evm_program::id(), AccountSharedData::default()),
];
let mut feature_set = FeatureSet::all_enabled();
feature_set.deactivate(&tx_wide_compute_cap::id());
feature_set.deactivate(&requestable_heap_size::id());
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
invoke_context.push(&[], &[0]).unwrap();

View File

@ -252,7 +252,9 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
// Convert AccountInfos into Accounts
let mut accounts = Vec::with_capacity(instruction_accounts.len());
for instruction_account in instruction_accounts.iter() {
let account_key = invoke_context.get_account_key_at_index(instruction_account.index);
let account_key = invoke_context
.transaction_context
.get_key_of_account_at_index(instruction_account.index);
let account_info = account_infos
.iter()
.find(|account_info| account_info.unsigned_key() == account_key)
@ -260,6 +262,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
.unwrap();
{
let mut account = invoke_context
.transaction_context
.get_account_at_index(instruction_account.index)
.borrow_mut();
account.copy_into_owner_from_slice(account_info.owner.as_ref());
@ -284,7 +287,9 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
// Copy writeable account modifications back into the caller's AccountInfos
for (account_index, account_info) in accounts.into_iter() {
let account = invoke_context.get_account_at_index(account_index);
let account = invoke_context
.transaction_context
.get_account_at_index(account_index);
let account_borrow = account.borrow();
**account_info.try_borrow_mut_lamports().unwrap() = account_borrow.lamports();
let mut data = account_info.try_borrow_mut_data()?;

View File

@ -323,6 +323,7 @@ mod tests {
bpf_loader,
entrypoint::deserialize,
instruction::AccountMeta,
transaction_context::TransactionContext,
},
std::{
cell::RefCell,
@ -452,7 +453,8 @@ mod tests {
let program_indices = [0];
let preparation =
prepare_mock_invoke_context(transaction_accounts.clone(), instruction_accounts);
let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]);
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.unwrap();

View File

@ -5,7 +5,7 @@ use {
alloc::Alloc,
solana_program_runtime::{
ic_logger_msg, ic_msg,
invoke_context::{ComputeMeter, InstructionAccount, InvokeContext},
invoke_context::{ComputeMeter, InvokeContext},
stable_log,
},
solana_rbpf::{
@ -41,6 +41,7 @@ use {
Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH,
},
sysvar::{self, Sysvar, SysvarId},
transaction_context::InstructionAccount,
},
std::{
alloc::Layout,
@ -2214,8 +2215,12 @@ where
accounts.push((*program_account_index, None));
for instruction_account in instruction_accounts.iter() {
let account = invoke_context.get_account_at_index(instruction_account.index);
let account_key = invoke_context.get_account_key_at_index(instruction_account.index);
let account = invoke_context
.transaction_context
.get_account_at_index(instruction_account.index);
let account_key = invoke_context
.transaction_context
.get_key_of_account_at_index(instruction_account.index);
if account.borrow().executable() {
// Use the known account
accounts.push((instruction_account.index, None));
@ -2396,6 +2401,7 @@ fn call<'a, 'b: 'a>(
for (callee_account_index, caller_account) in accounts.iter_mut() {
if let Some(caller_account) = caller_account {
let callee_account = invoke_context
.transaction_context
.get_account_at_index(*callee_account_index)
.borrow();
*caller_account.lamports = callee_account.lamports();
@ -2669,6 +2675,7 @@ mod tests {
},
solana_sdk::{
account::AccountSharedData, bpf_loader, fee_calculator::FeeCalculator, hash::hashv,
transaction_context::TransactionContext,
},
std::str::FromStr,
};
@ -2979,9 +2986,11 @@ mod tests {
#[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")]
fn test_syscall_sol_panic() {
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut syscall_panic = SyscallPanic {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3050,9 +3059,11 @@ mod tests {
#[test]
fn test_syscall_sol_log() {
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let mut syscall_sol_log = SyscallLog {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3148,9 +3159,11 @@ mod tests {
#[test]
fn test_syscall_sol_log_u64() {
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let cost = invoke_context.get_compute_budget().log_64_units;
let mut syscall_sol_log_u64 = SyscallLogU64 {
@ -3184,9 +3197,11 @@ mod tests {
#[test]
fn test_syscall_sol_pubkey() {
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let cost = invoke_context.get_compute_budget().log_pubkey_units;
let mut syscall_sol_pubkey = SyscallLogPubkey {
@ -3390,10 +3405,14 @@ mod tests {
fn test_syscall_sha256() {
let config = Config::default();
let program_id = Pubkey::new_unique();
let program_account =
RefCell::new(AccountSharedData::new(0, 0, &bpf_loader_deprecated::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(
program_id,
AccountSharedData::new(0, 0, &bpf_loader_deprecated::id()),
)],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let bytes1 = "Gaggablaghblagh!";
@ -3511,11 +3530,55 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_syscall_get_sysvar() {
let config = Config::default();
let src_clock = Clock {
slot: 1,
epoch_start_timestamp: 2,
epoch: 3,
leader_schedule_epoch: 4,
unix_timestamp: 5,
};
let mut data_clock = vec![];
bincode::serialize_into(&mut data_clock, &src_clock).unwrap();
let src_epochschedule = EpochSchedule {
slots_per_epoch: 1,
leader_schedule_slot_offset: 2,
warmup: false,
first_normal_epoch: 3,
first_normal_slot: 4,
};
let mut data_epochschedule = vec![];
bincode::serialize_into(&mut data_epochschedule, &src_epochschedule).unwrap();
let src_fees = Fees {
fee_calculator: FeeCalculator {
lamports_per_signature: 1,
},
};
let mut data_fees = vec![];
bincode::serialize_into(&mut data_fees, &src_fees).unwrap();
let src_rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 2.0,
burn_percent: 3,
};
let mut data_rent = vec![];
bincode::serialize_into(&mut data_rent, &src_rent).unwrap();
let sysvars = [
(sysvar::clock::id(), data_clock),
(sysvar::epoch_schedule::id(), data_epochschedule),
(sysvar::fees::id(), data_fees),
(sysvar::rent::id(), data_rent),
];
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
// Test clock sysvar
{
@ -3536,20 +3599,6 @@ mod tests {
&config,
)
.unwrap();
let src_clock = Clock {
slot: 1,
epoch_start_timestamp: 2,
epoch: 3,
leader_schedule_epoch: 4,
unix_timestamp: 5,
};
let mut data = vec![];
bincode::serialize_into(&mut data, &src_clock).unwrap();
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let sysvars = [(sysvar::clock::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
let mut syscall = SyscallGetClockSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3579,20 +3628,6 @@ mod tests {
&config,
)
.unwrap();
let src_epochschedule = EpochSchedule {
slots_per_epoch: 1,
leader_schedule_slot_offset: 2,
warmup: false,
first_normal_epoch: 3,
first_normal_slot: 4,
};
let mut data = vec![];
bincode::serialize_into(&mut data, &src_epochschedule).unwrap();
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let sysvars = [(sysvar::epoch_schedule::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
let mut syscall = SyscallGetEpochScheduleSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3612,7 +3647,6 @@ mod tests {
}
// Test fees sysvar
#[allow(deprecated)]
{
let got_fees = Fees::default();
let got_fees_va = 0x100000000;
@ -3631,18 +3665,6 @@ mod tests {
&config,
)
.unwrap();
let src_fees = Fees {
fee_calculator: FeeCalculator {
lamports_per_signature: 1,
},
};
let mut data = vec![];
bincode::serialize_into(&mut data, &src_fees).unwrap();
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let sysvars = [(sysvar::fees::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
let mut syscall = SyscallGetFeesSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3672,18 +3694,6 @@ mod tests {
&config,
)
.unwrap();
let src_rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 2.0,
burn_percent: 3,
};
let mut data = vec![];
bincode::serialize_into(&mut data, &src_rent).unwrap();
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let sysvars = [(sysvar::rent::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context.push(&[], &[0]).unwrap();
let mut syscall = SyscallGetRentSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
};
@ -3812,9 +3822,11 @@ mod tests {
// These tests duplicate the direct tests in solana_program::pubkey
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let address = bpf_loader_upgradeable::id();
@ -3922,9 +3934,11 @@ mod tests {
#[test]
fn test_find_program_address() {
let program_id = Pubkey::new_unique();
let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id()));
let accounts = [(program_id, program_account)];
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
let transaction_context = TransactionContext::new(
vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))],
1,
);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.push(&[], &[0]).unwrap();
let cost = invoke_context
.get_compute_budget()

View File

@ -1308,6 +1308,7 @@ mod tests {
native_token,
pubkey::Pubkey,
system_program,
transaction_context::TransactionContext,
},
solana_vote_program::vote_state,
std::{cell::RefCell, iter::FromIterator},
@ -4998,7 +4999,8 @@ mod tests {
#[test]
fn test_merge() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5108,7 +5110,8 @@ mod tests {
#[test]
fn test_merge_self_fails() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let stake_address = Pubkey::new_unique();
let authority_pubkey = Pubkey::new_unique();
let signers = HashSet::from_iter(vec![authority_pubkey]);
@ -5153,7 +5156,8 @@ mod tests {
#[test]
fn test_merge_incorrect_authorized_staker() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5222,7 +5226,8 @@ mod tests {
#[test]
fn test_merge_invalid_account_data() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5272,7 +5277,8 @@ mod tests {
#[test]
fn test_merge_fake_stake_source() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let stake_pubkey = solana_sdk::pubkey::new_rand();
let source_stake_pubkey = solana_sdk::pubkey::new_rand();
let authorized_pubkey = solana_sdk::pubkey::new_rand();
@ -5314,7 +5320,8 @@ mod tests {
#[test]
fn test_merge_active_stake() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let base_lamports = 4242424242;
let stake_address = Pubkey::new_unique();
let source_address = Pubkey::new_unique();
@ -5936,7 +5943,8 @@ mod tests {
#[test]
fn test_things_can_merge() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let good_stake = Stake {
credits_observed: 4242,
delegation: Delegation {
@ -6034,7 +6042,8 @@ mod tests {
#[test]
fn test_metas_can_merge_pre_v4() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Identical Metas can merge
assert!(MergeKind::metas_can_merge(
&invoke_context,
@ -6120,7 +6129,8 @@ mod tests {
#[test]
fn test_metas_can_merge_v4() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Identical Metas can merge
assert!(MergeKind::metas_can_merge(
&invoke_context,
@ -6266,7 +6276,8 @@ mod tests {
#[test]
fn test_merge_kind_get_if_mergeable() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let authority_pubkey = Pubkey::new_unique();
let initial_lamports = 4242424242;
let rent = Rent::default();
@ -6498,7 +6509,8 @@ mod tests {
#[test]
fn test_merge_kind_merge() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let lamports = 424242;
let meta = Meta {
rent_exempt_reserve: 42,
@ -6576,7 +6588,8 @@ mod tests {
#[test]
fn test_active_stake_merge() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let delegation_a = 4_242_424_242u64;
let delegation_b = 6_200_000_000u64;
let credits_a = 124_521_000u64;

View File

@ -16,6 +16,7 @@ use {
},
solana_sdk::{
account::AccountSharedData, bpf_loader, instruction::AccountMeta, pubkey::Pubkey,
transaction_context::TransactionContext,
},
std::{
fs::File,
@ -210,7 +211,8 @@ native machine code before execting it in the virtual machine.",
};
let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts);
let program_indices = [0, 1];
let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]);
let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context
.push(&preparation.instruction_accounts, &program_indices)
.unwrap();

View File

@ -37,6 +37,7 @@ use {
system_program,
sysvar::{self, instructions::construct_instructions_data},
transaction::{Result, SanitizedTransaction, TransactionError},
transaction_context::TransactionAccount,
},
std::{
cmp::Reverse,
@ -105,12 +106,11 @@ pub struct Accounts {
}
// for the load instructions
pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>;
pub type TransactionRent = u64;
pub type TransactionProgramIndices = Vec<Vec<usize>>;
#[derive(PartialEq, Debug, Clone)]
pub struct LoadedTransaction {
pub accounts: TransactionAccounts,
pub accounts: Vec<TransactionAccount>,
pub program_indices: TransactionProgramIndices,
pub rent: TransactionRent,
pub rent_debits: RentDebits,
@ -385,7 +385,7 @@ impl Accounts {
fn load_executable_accounts(
&self,
ancestors: &Ancestors,
accounts: &mut Vec<(Pubkey, AccountSharedData)>,
accounts: &mut Vec<TransactionAccount>,
mut program_account_index: usize,
error_counters: &mut ErrorCounters,
) -> Result<Vec<usize>> {
@ -606,7 +606,7 @@ impl Accounts {
&self,
slot: Slot,
program_id: Option<&Pubkey>,
) -> Vec<(Pubkey, AccountSharedData)> {
) -> Vec<TransactionAccount> {
self.scan_slot(slot, |stored_account| {
let hit = match program_id {
None => true,
@ -721,7 +721,7 @@ impl Accounts {
}
fn load_while_filtering<F: Fn(&AccountSharedData) -> bool>(
collector: &mut Vec<(Pubkey, AccountSharedData)>,
collector: &mut Vec<TransactionAccount>,
some_account_tuple: Option<(&Pubkey, AccountSharedData, Slot)>,
filter: F,
) {
@ -739,11 +739,11 @@ impl Accounts {
bank_id: BankId,
program_id: &Pubkey,
config: &ScanConfig,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
self.accounts_db.scan_accounts(
ancestors,
bank_id,
|collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| {
|collector: &mut Vec<TransactionAccount>, some_account_tuple| {
Self::load_while_filtering(collector, some_account_tuple, |account| {
account.owner() == program_id
})
@ -759,11 +759,11 @@ impl Accounts {
program_id: &Pubkey,
filter: F,
config: &ScanConfig,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
self.accounts_db.scan_accounts(
ancestors,
bank_id,
|collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| {
|collector: &mut Vec<TransactionAccount>, some_account_tuple| {
Self::load_while_filtering(collector, some_account_tuple, |account| {
account.owner() == program_id && filter(account)
})
@ -796,9 +796,9 @@ impl Accounts {
}
fn maybe_abort_scan(
result: ScanResult<Vec<(Pubkey, AccountSharedData)>>,
result: ScanResult<Vec<TransactionAccount>>,
config: &ScanConfig,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
if config.is_aborted() {
ScanResult::Err(ScanError::Aborted(
"The accumulated scan results exceeded the limit".to_string(),
@ -816,7 +816,7 @@ impl Accounts {
filter: F,
config: &ScanConfig,
byte_limit_for_scan: Option<usize>,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
let sum = AtomicUsize::default();
let config = config.recreate_with_abort();
let result = self
@ -825,7 +825,7 @@ impl Accounts {
ancestors,
bank_id,
*index_key,
|collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| {
|collector: &mut Vec<TransactionAccount>, some_account_tuple| {
Self::load_while_filtering(collector, some_account_tuple, |account| {
let use_account = filter(account);
if use_account
@ -887,13 +887,13 @@ impl Accounts {
&self,
ancestors: &Ancestors,
range: R,
) -> Vec<(Pubkey, AccountSharedData)> {
) -> Vec<TransactionAccount> {
self.accounts_db.range_scan_accounts(
"load_to_collect_rent_eagerly_scan_elapsed",
ancestors,
range,
&ScanConfig::new(true),
|collector: &mut Vec<(Pubkey, AccountSharedData)>, option| {
|collector: &mut Vec<TransactionAccount>, option| {
Self::load_while_filtering(collector, option, |_| true)
},
)
@ -1277,7 +1277,7 @@ mod tests {
fn load_accounts_with_fee_and_rent(
tx: Transaction,
ka: &[(Pubkey, AccountSharedData)],
ka: &[TransactionAccount],
lamports_per_signature: u64,
rent_collector: &RentCollector,
error_counters: &mut ErrorCounters,
@ -1310,7 +1310,7 @@ mod tests {
fn load_accounts_with_fee(
tx: Transaction,
ka: &[(Pubkey, AccountSharedData)],
ka: &[TransactionAccount],
lamports_per_signature: u64,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionLoadResult> {
@ -1325,7 +1325,7 @@ mod tests {
fn load_accounts(
tx: Transaction,
ka: &[(Pubkey, AccountSharedData)],
ka: &[TransactionAccount],
error_counters: &mut ErrorCounters,
) -> Vec<TransactionLoadResult> {
load_accounts_with_fee(tx, ka, 0, error_counters)
@ -1390,7 +1390,7 @@ mod tests {
#[test]
fn test_load_accounts_no_account_0_exists() {
let accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1416,7 +1416,7 @@ mod tests {
#[test]
fn test_load_accounts_unknown_program_id() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1450,7 +1450,7 @@ mod tests {
#[test]
fn test_load_accounts_insufficient_funds() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1486,7 +1486,7 @@ mod tests {
#[test]
fn test_load_accounts_invalid_account_for_fee() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1588,7 +1588,7 @@ mod tests {
#[test]
fn test_load_accounts_no_loaders() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1629,7 +1629,7 @@ mod tests {
#[test]
fn test_load_accounts_max_call_depth() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1695,7 +1695,7 @@ mod tests {
#[test]
fn test_load_accounts_bad_owner() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1730,7 +1730,7 @@ mod tests {
#[test]
fn test_load_accounts_not_executable() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1764,7 +1764,7 @@ mod tests {
#[test]
fn test_load_accounts_multiple_loaders() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1860,7 +1860,7 @@ mod tests {
#[test]
fn test_load_accounts_executable_with_write_lock() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -1916,7 +1916,7 @@ mod tests {
#[test]
fn test_load_accounts_upgradeable_with_write_lock() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();
@ -2013,7 +2013,7 @@ mod tests {
#[test]
fn test_load_accounts_programdata_with_write_lock() {
let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new();
let mut accounts: Vec<TransactionAccount> = Vec::new();
let mut error_counters = ErrorCounters::default();
let keypair = Keypair::new();

View File

@ -37,7 +37,7 @@
use solana_sdk::recent_blockhashes_account;
use {
crate::{
accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult},
accounts::{AccountAddressFilter, Accounts, TransactionLoadResult},
accounts_db::{
AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages,
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
@ -74,10 +74,7 @@ use {
solana_metrics::{inc_new_counter_debug, inc_new_counter_info},
solana_program_runtime::{
instruction_recorder::InstructionRecorder,
invoke_context::{
BuiltinProgram, Executor, Executors, ProcessInstructionWithContext,
TransactionAccountRefCells,
},
invoke_context::{BuiltinProgram, Executor, Executors, ProcessInstructionWithContext},
log_collector::LogCollector,
timings::ExecuteDetailsTimings,
},
@ -127,6 +124,7 @@ use {
Result, SanitizedTransaction, Transaction, TransactionError,
TransactionVerificationMode, VersionedTransaction,
},
transaction_context::{TransactionAccount, TransactionContext},
},
solana_stake_program::stake_state::{
self, InflationPointCalculationEvent, PointValue, StakeState,
@ -515,7 +513,7 @@ pub struct TransactionResults {
pub struct TransactionSimulationResult {
pub result: Result<()>,
pub logs: TransactionLogMessages,
pub post_simulation_accounts: Vec<(Pubkey, AccountSharedData)>,
pub post_simulation_accounts: Vec<TransactionAccount>,
pub units_consumed: u64,
}
pub struct TransactionBalancesSet {
@ -654,7 +652,7 @@ impl NonceFull {
pub fn from_partial(
partial: NoncePartial,
message: &SanitizedMessage,
accounts: &[(Pubkey, AccountSharedData)],
accounts: &[TransactionAccount],
rent_debits: &RentDebits,
) -> Result<Self> {
let fee_payer = (0..message.account_keys_len()).find_map(|i| {
@ -3287,7 +3285,7 @@ impl Bank {
pub fn check_transaction_for_nonce(
&self,
tx: &SanitizedTransaction,
) -> Option<(Pubkey, AccountSharedData)> {
) -> Option<TransactionAccount> {
tx.get_durable_nonce(self.feature_set.is_active(&nonce_must_be_writable::id()))
.and_then(|nonce_address| {
self.get_account_with_fixed_root(nonce_address)
@ -3404,31 +3402,11 @@ impl Bank {
}
}
/// Converts Accounts into RefCell<AccountSharedData>, this involves moving
/// ownership by draining the source
fn accounts_to_refcells(accounts: &mut TransactionAccounts) -> TransactionAccountRefCells {
accounts
.drain(..)
.map(|(pubkey, account)| (pubkey, RefCell::new(account)))
.collect()
}
/// Converts back from RefCell<AccountSharedData> to AccountSharedData, this involves moving
/// ownership by draining the sources
fn refcells_to_accounts(
accounts: &mut TransactionAccounts,
mut account_refcells: TransactionAccountRefCells,
) {
for (pubkey, account_refcell) in account_refcells.drain(..) {
accounts.push((pubkey, account_refcell.into_inner()))
}
}
/// Get any cached executors needed by the transaction
fn get_executors(
&self,
message: &SanitizedMessage,
accounts: &[(Pubkey, AccountSharedData)],
accounts: &[TransactionAccount],
program_indices: &[Vec<usize>],
) -> Rc<RefCell<Executors>> {
let mut num_executors = message.account_keys_len();
@ -3573,8 +3551,12 @@ impl Bank {
&loaded_transaction.program_indices,
);
let account_refcells =
Self::accounts_to_refcells(&mut loaded_transaction.accounts);
let mut transaction_accounts = Vec::new();
std::mem::swap(&mut loaded_transaction.accounts, &mut transaction_accounts);
let transaction_context = TransactionContext::new(
transaction_accounts,
compute_budget.max_invoke_depth,
);
let instruction_recorder = if enable_cpi_recording {
Some(InstructionRecorder::new_ref(
@ -3598,7 +3580,7 @@ impl Bank {
&self.builtin_programs.vec,
legacy_message,
&loaded_transaction.program_indices,
&account_refcells,
&transaction_context,
self.rent_collector.rent,
log_collector.clone(),
executors.clone(),
@ -3637,10 +3619,7 @@ impl Bank {
}),
);
Self::refcells_to_accounts(
&mut loaded_transaction.accounts,
account_refcells,
);
loaded_transaction.accounts = transaction_context.deconstruct();
if process_result.is_ok() {
self.update_executors(executors);
@ -4993,7 +4972,7 @@ impl Bank {
&self,
program_id: &Pubkey,
config: &ScanConfig,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
self.rc
.accounts
.load_by_program(&self.ancestors, self.bank_id, program_id, config)
@ -5004,7 +4983,7 @@ impl Bank {
program_id: &Pubkey,
filter: F,
config: &ScanConfig,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
self.rc.accounts.load_by_program_with_filter(
&self.ancestors,
self.bank_id,
@ -5020,7 +4999,7 @@ impl Bank {
filter: F,
config: &ScanConfig,
byte_limit_for_scan: Option<usize>,
) -> ScanResult<Vec<(Pubkey, AccountSharedData)>> {
) -> ScanResult<Vec<TransactionAccount>> {
self.rc.accounts.load_by_index_key_with_filter(
&self.ancestors,
self.bank_id,
@ -5044,7 +5023,7 @@ impl Bank {
pub fn get_program_accounts_modified_since_parent(
&self,
program_id: &Pubkey,
) -> Vec<(Pubkey, AccountSharedData)> {
) -> Vec<TransactionAccount> {
self.rc
.accounts
.load_by_program_slot(self.slot(), Some(program_id))
@ -5060,7 +5039,7 @@ impl Bank {
.get_logs_for_address(address)
}
pub fn get_all_accounts_modified_since_parent(&self) -> Vec<(Pubkey, AccountSharedData)> {
pub fn get_all_accounts_modified_since_parent(&self) -> Vec<TransactionAccount> {
self.rc.accounts.load_by_program_slot(self.slot(), None)
}
@ -6722,8 +6701,7 @@ pub(crate) mod tests {
mock_program_id: Pubkey,
generic_rent_due_for_system_account: u64,
) {
let mut account_pairs: Vec<(Pubkey, AccountSharedData)> =
Vec::with_capacity(keypairs.len() - 1);
let mut account_pairs: Vec<TransactionAccount> = Vec::with_capacity(keypairs.len() - 1);
account_pairs.push((
keypairs[0].pubkey(),
AccountSharedData::new(

View File

@ -3,9 +3,7 @@ use {
solana_measure::measure::Measure,
solana_program_runtime::{
instruction_recorder::InstructionRecorder,
invoke_context::{
BuiltinProgram, Executors, InstructionAccount, InvokeContext, TransactionAccountRefCell,
},
invoke_context::{BuiltinProgram, Executors, InvokeContext},
log_collector::LogCollector,
timings::ExecuteDetailsTimings,
},
@ -20,6 +18,7 @@ use {
rent::Rent,
sysvar::instructions,
transaction::TransactionError,
transaction_context::{InstructionAccount, TransactionContext},
},
std::{cell::RefCell, rc::Rc, sync::Arc},
};
@ -54,7 +53,7 @@ impl MessageProcessor {
builtin_programs: &[BuiltinProgram],
message: &Message,
program_indices: &[Vec<usize>],
accounts: &[TransactionAccountRefCell],
transaction_context: &TransactionContext,
rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>,
executors: Rc<RefCell<Executors>>,
@ -67,8 +66,8 @@ impl MessageProcessor {
lamports_per_signature: u64,
) -> Result<ProcessedMessageInfo, TransactionError> {
let mut invoke_context = InvokeContext::new(
transaction_context,
rent,
accounts,
builtin_programs,
sysvars,
log_collector,
@ -99,15 +98,18 @@ impl MessageProcessor {
// Fixup the special instructions key if present
// before the account pre-values are taken care of
for (pubkey, account) in accounts.iter().take(message.account_keys.len()) {
if instructions::check_id(pubkey) {
let mut mut_account_ref = account.borrow_mut();
instructions::store_current_index(
mut_account_ref.data_as_mut_slice(),
instruction_index as u16,
);
break;
}
if let Some(account_index) = invoke_context
.transaction_context
.find_index_of_account(&instructions::id())
{
let mut mut_account_ref = invoke_context
.transaction_context
.get_account_at_index(account_index)
.borrow_mut();
instructions::store_current_index(
mut_account_ref.data_as_mut_slice(),
instruction_index as u16,
);
}
let instruction_accounts = instruction
@ -221,39 +223,38 @@ mod tests {
let accounts = vec![
(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(100, 1, &mock_system_program_id)),
AccountSharedData::new(100, 1, &mock_system_program_id),
),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(0, 1, &mock_system_program_id)),
AccountSharedData::new(0, 1, &mock_system_program_id),
),
(
mock_system_program_id,
RefCell::new(create_loadable_account_for_test("mock_system_program")),
create_loadable_account_for_test("mock_system_program"),
),
];
let transaction_context = TransactionContext::new(accounts, 1);
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
AccountMeta::new(accounts[0].0, true),
AccountMeta::new_readonly(accounts[1].0, false),
AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), true),
AccountMeta::new_readonly(*transaction_context.get_key_of_account_at_index(1), false),
];
let message = Message::new(
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::Correct,
account_metas.clone(),
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -266,8 +267,20 @@ mod tests {
0,
);
assert!(result.is_ok());
assert_eq!(accounts[0].1.borrow().lamports(), 100);
assert_eq!(accounts[1].1.borrow().lamports(), 0);
assert_eq!(
transaction_context
.get_account_at_index(0)
.borrow()
.lamports(),
100
);
assert_eq!(
transaction_context
.get_account_at_index(1)
.borrow()
.lamports(),
0
);
let message = Message::new(
&[Instruction::new_with_bincode(
@ -275,14 +288,13 @@ mod tests {
&MockSystemInstruction::AttemptCredit { lamports: 50 },
account_metas.clone(),
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -308,14 +320,13 @@ mod tests {
&MockSystemInstruction::AttemptDataChange { data: 50 },
account_metas,
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors,
@ -425,25 +436,24 @@ mod tests {
let accounts = vec![
(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(100, 1, &mock_program_id)),
AccountSharedData::new(100, 1, &mock_program_id),
),
(
solana_sdk::pubkey::new_rand(),
RefCell::new(AccountSharedData::new(0, 1, &mock_program_id)),
AccountSharedData::new(0, 1, &mock_program_id),
),
(
mock_program_id,
RefCell::new(create_loadable_account_for_test("mock_system_program")),
create_loadable_account_for_test("mock_system_program"),
),
];
let transaction_context = TransactionContext::new(accounts, 1);
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
AccountMeta::new(accounts[0].0, true),
AccountMeta::new(accounts[1].0, false),
AccountMeta::new(accounts[0].0, false),
AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), true),
AccountMeta::new(*transaction_context.get_key_of_account_at_index(1), false),
AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), false),
];
// Try to borrow mut the same account
@ -453,13 +463,13 @@ mod tests {
&MockSystemInstruction::BorrowFail,
account_metas.clone(),
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -486,13 +496,13 @@ mod tests {
&MockSystemInstruction::MultiBorrowMut,
account_metas.clone(),
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors.clone(),
@ -516,13 +526,13 @@ mod tests {
},
account_metas,
)],
Some(&accounts[0].0),
Some(transaction_context.get_key_of_account_at_index(0)),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
&transaction_context,
rent_collector.rent,
None,
executors,
@ -535,9 +545,24 @@ mod tests {
0,
);
assert!(result.is_ok());
assert_eq!(accounts[0].1.borrow().lamports(), 80);
assert_eq!(accounts[1].1.borrow().lamports(), 20);
assert_eq!(accounts[0].1.borrow().data(), &vec![42]);
assert_eq!(
transaction_context
.get_account_at_index(0)
.borrow()
.lamports(),
80
);
assert_eq!(
transaction_context
.get_account_at_index(1)
.borrow()
.lamports(),
20
);
assert_eq!(
transaction_context.get_account_at_index(0).borrow().data(),
&vec![42]
);
}
#[test]
@ -560,9 +585,10 @@ mod tests {
let mut mock_program_account = AccountSharedData::new(1, 0, &native_loader::id());
mock_program_account.set_executable(true);
let accounts = vec![
(secp256k1_program::id(), RefCell::new(secp256k1_account)),
(mock_program_id, RefCell::new(mock_program_account)),
(secp256k1_program::id(), secp256k1_account),
(mock_program_id, mock_program_account),
];
let transaction_context = TransactionContext::new(accounts, 1);
let message = Message::new(
&[
@ -574,12 +600,11 @@ mod tests {
],
None,
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&[vec![0], vec![1]],
&accounts,
&transaction_context,
RentCollector::default().rent,
None,
Rc::new(RefCell::new(Executors::default())),

View File

@ -326,32 +326,25 @@ mod test {
nonce::{self, State},
nonce_account::{create_account, verify_nonce_account},
system_instruction::SystemError,
transaction_context::TransactionContext,
},
};
fn with_test_keyed_account<F>(lamports: u64, signer: bool, f: F)
fn with_mockup<F>(lamports: u64, signer: bool, mut f: F)
where
F: Fn(&KeyedAccount),
F: FnMut(&mut InvokeContext, &KeyedAccount),
{
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let pubkey = Pubkey::new_unique();
let account = create_account(lamports);
let keyed_account = KeyedAccount::new(&pubkey, signer, &account);
f(&keyed_account)
f(&mut invoke_context, &keyed_account)
}
fn create_test_blockhash(seed: usize) -> (Hash, u64) {
(
hash(&bincode::serialize(&seed).unwrap()),
(seed as u64).saturating_mul(100),
)
}
fn create_invoke_context_with_blockhash<'a>(seed: usize) -> InvokeContext<'a> {
let mut invoke_context = InvokeContext::new_mock(&[], &[]);
let (blockhash, lamports_per_signature) = create_test_blockhash(seed);
invoke_context.blockhash = blockhash;
invoke_context.lamports_per_signature = lamports_per_signature;
invoke_context
fn set_invoke_context_blockhash(invoke_context: &mut InvokeContext, seed: usize) {
invoke_context.blockhash = hash(&bincode::serialize(&seed).unwrap());
invoke_context.lamports_per_signature = (seed as u64).saturating_mul(100);
}
#[test]
@ -366,7 +359,7 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |keyed_account| {
with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| {
let data = nonce::state::Data {
authority: *keyed_account.unsigned_key(),
..nonce::state::Data::default()
@ -378,10 +371,10 @@ mod test {
.convert_to_current();
// New is in Uninitialzed state
assert_eq!(state, State::Uninitialized);
let invoke_context = create_invoke_context_with_blockhash(95);
set_invoke_context_blockhash(invoke_context, 95);
let authorized = keyed_account.unsigned_key();
keyed_account
.initialize_nonce_account(authorized, &rent, &invoke_context)
.initialize_nonce_account(authorized, &rent, invoke_context)
.unwrap();
let state = AccountUtilsState::<Versions>::state(keyed_account)
.unwrap()
@ -393,9 +386,9 @@ mod test {
);
// First nonce instruction drives state from Uninitialized to Initialized
assert_eq!(state, State::Initialized(data.clone()));
let invoke_context = create_invoke_context_with_blockhash(63);
set_invoke_context_blockhash(invoke_context, 63);
keyed_account
.advance_nonce_account(&signers, &invoke_context)
.advance_nonce_account(&signers, invoke_context)
.unwrap();
let state = AccountUtilsState::<Versions>::state(keyed_account)
.unwrap()
@ -407,9 +400,9 @@ mod test {
);
// Second nonce instruction consumes and replaces stored nonce
assert_eq!(state, State::Initialized(data.clone()));
let invoke_context = create_invoke_context_with_blockhash(31);
set_invoke_context_blockhash(invoke_context, 31);
keyed_account
.advance_nonce_account(&signers, &invoke_context)
.advance_nonce_account(&signers, invoke_context)
.unwrap();
let state = AccountUtilsState::<Versions>::state(keyed_account)
.unwrap()
@ -421,8 +414,8 @@ mod test {
);
// Third nonce instruction for fun and profit
assert_eq!(state, State::Initialized(data));
with_test_keyed_account(42, false, |to_keyed| {
let invoke_context = create_invoke_context_with_blockhash(0);
with_mockup(42, false, |_invoke_context, to_keyed| {
set_invoke_context_blockhash(invoke_context, 0);
let withdraw_lamports = keyed_account.account.borrow().lamports();
let expect_nonce_lamports =
keyed_account.account.borrow().lamports() - withdraw_lamports;
@ -433,7 +426,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
// Empties Account balance
@ -459,11 +452,11 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
let invoke_context = create_invoke_context_with_blockhash(31);
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
set_invoke_context_blockhash(invoke_context, 31);
let authority = *nonce_account.unsigned_key();
nonce_account
.initialize_nonce_account(&authority, &rent, &invoke_context)
.initialize_nonce_account(&authority, &rent, invoke_context)
.unwrap();
let pubkey = *nonce_account.account.borrow().owner();
let nonce_account = KeyedAccount::new(&pubkey, false, nonce_account.account);
@ -477,9 +470,9 @@ mod test {
);
assert_eq!(state, State::Initialized(data));
let signers = HashSet::new();
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let result = nonce_account.advance_nonce_account(&signers, &invoke_context);
let result = nonce_account.advance_nonce_account(&signers, invoke_context);
assert_eq!(result, Err(InstructionError::MissingRequiredSignature),);
})
}
@ -491,15 +484,15 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |keyed_account| {
with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| {
let mut signers = HashSet::new();
signers.insert(*keyed_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(63);
set_invoke_context_blockhash(invoke_context, 63);
let authorized = *keyed_account.unsigned_key();
keyed_account
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
let result = keyed_account.advance_nonce_account(&signers, &invoke_context);
let result = keyed_account.advance_nonce_account(&signers, invoke_context);
assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into()));
})
}
@ -511,11 +504,11 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |keyed_account| {
with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| {
let mut signers = HashSet::new();
signers.insert(*keyed_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(63);
let result = keyed_account.advance_nonce_account(&signers, &invoke_context);
set_invoke_context_blockhash(invoke_context, 63);
let result = keyed_account.advance_nonce_account(&signers, invoke_context);
assert_eq!(result, Err(InstructionError::InvalidAccountData));
})
}
@ -527,19 +520,19 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
with_test_keyed_account(42, true, |nonce_authority| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
with_mockup(42, true, |_invoke_context, nonce_authority| {
let mut signers = HashSet::new();
signers.insert(*nonce_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(63);
set_invoke_context_blockhash(invoke_context, 63);
let authorized = *nonce_authority.unsigned_key();
nonce_account
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
let mut signers = HashSet::new();
signers.insert(*nonce_authority.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(31);
let result = nonce_account.advance_nonce_account(&signers, &invoke_context);
set_invoke_context_blockhash(invoke_context, 31);
let result = nonce_account.advance_nonce_account(&signers, invoke_context);
assert_eq!(result, Ok(()));
});
});
@ -552,16 +545,16 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
with_test_keyed_account(42, false, |nonce_authority| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
with_mockup(42, false, |_invoke_context, nonce_authority| {
let mut signers = HashSet::new();
signers.insert(*nonce_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(63);
set_invoke_context_blockhash(invoke_context, 63);
let authorized = *nonce_authority.unsigned_key();
nonce_account
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
let result = nonce_account.advance_nonce_account(&signers, &invoke_context);
let result = nonce_account.advance_nonce_account(&signers, invoke_context);
assert_eq!(result, Err(InstructionError::MissingRequiredSignature),);
});
});
@ -574,15 +567,15 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let withdraw_lamports = nonce_keyed.account.borrow().lamports();
let expect_nonce_lamports =
nonce_keyed.account.borrow().lamports() - withdraw_lamports;
@ -593,7 +586,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
@ -620,21 +613,21 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, false, |nonce_keyed| {
with_mockup(min_lamports + 42, false, |invoke_context, nonce_keyed| {
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let signers = HashSet::new();
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let lamports = nonce_keyed.account.borrow().lamports();
let result = nonce_keyed.withdraw_nonce_account(
lamports,
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(InstructionError::MissingRequiredSignature),);
})
@ -648,22 +641,22 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let lamports = nonce_keyed.account.borrow().lamports() + 1;
let result = nonce_keyed.withdraw_nonce_account(
lamports,
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(InstructionError::InsufficientFunds));
})
@ -677,11 +670,11 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let withdraw_lamports = nonce_keyed.account.borrow().lamports() / 2;
let nonce_expect_lamports =
nonce_keyed.account.borrow().lamports() - withdraw_lamports;
@ -692,7 +685,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
@ -714,7 +707,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
@ -737,13 +730,13 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(31);
set_invoke_context_blockhash(invoke_context, 31);
let authority = *nonce_keyed.unsigned_key();
nonce_keyed
.initialize_nonce_account(&authority, &rent, &invoke_context)
.initialize_nonce_account(&authority, &rent, invoke_context)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
.unwrap()
@ -754,7 +747,7 @@ mod test {
invoke_context.lamports_per_signature,
);
assert_eq!(state, State::Initialized(data.clone()));
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let withdraw_lamports = nonce_keyed.account.borrow().lamports() - min_lamports;
let nonce_expect_lamports =
nonce_keyed.account.borrow().lamports() - withdraw_lamports;
@ -765,7 +758,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
@ -782,7 +775,7 @@ mod test {
nonce_expect_lamports
);
assert_eq!(to_keyed.account.borrow().lamports(), to_expect_lamports);
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let withdraw_lamports = nonce_keyed.account.borrow().lamports();
let nonce_expect_lamports =
nonce_keyed.account.borrow().lamports() - withdraw_lamports;
@ -793,7 +786,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
)
.unwrap();
let state = AccountUtilsState::<Versions>::state(nonce_keyed)
@ -816,13 +809,13 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
let invoke_context = create_invoke_context_with_blockhash(0);
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
set_invoke_context_blockhash(invoke_context, 0);
let authorized = *nonce_keyed.unsigned_key();
nonce_keyed
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
with_test_keyed_account(42, false, |to_keyed| {
with_mockup(42, false, |_invoke_context, to_keyed| {
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let withdraw_lamports = nonce_keyed.account.borrow().lamports();
@ -831,7 +824,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into()));
})
@ -845,14 +838,14 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
let invoke_context = create_invoke_context_with_blockhash(95);
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
set_invoke_context_blockhash(invoke_context, 95);
let authorized = *nonce_keyed.unsigned_key();
nonce_keyed
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
with_test_keyed_account(42, false, |to_keyed| {
let invoke_context = create_invoke_context_with_blockhash(63);
with_mockup(42, false, |_invoke_context, to_keyed| {
set_invoke_context_blockhash(invoke_context, 63);
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let withdraw_lamports = nonce_keyed.account.borrow().lamports() + 1;
@ -861,7 +854,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(InstructionError::InsufficientFunds));
})
@ -875,14 +868,14 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
let invoke_context = create_invoke_context_with_blockhash(95);
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
set_invoke_context_blockhash(invoke_context, 95);
let authorized = *nonce_keyed.unsigned_key();
nonce_keyed
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
with_test_keyed_account(42, false, |to_keyed| {
let invoke_context = create_invoke_context_with_blockhash(63);
with_mockup(42, false, |_invoke_context, to_keyed| {
set_invoke_context_blockhash(invoke_context, 63);
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let withdraw_lamports = nonce_keyed.account.borrow().lamports() - min_lamports + 1;
@ -891,7 +884,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(InstructionError::InsufficientFunds));
})
@ -905,14 +898,14 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| {
let invoke_context = create_invoke_context_with_blockhash(95);
with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| {
set_invoke_context_blockhash(invoke_context, 95);
let authorized = *nonce_keyed.unsigned_key();
nonce_keyed
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
with_test_keyed_account(55, false, |to_keyed| {
let invoke_context = create_invoke_context_with_blockhash(63);
with_mockup(55, false, |_invoke_context, to_keyed| {
set_invoke_context_blockhash(invoke_context, 63);
let mut signers = HashSet::new();
signers.insert(*nonce_keyed.signer_key().unwrap());
let withdraw_lamports = u64::MAX - 54;
@ -921,7 +914,7 @@ mod test {
to_keyed,
&rent,
&signers,
&invoke_context,
invoke_context,
);
assert_eq!(result, Err(InstructionError::InsufficientFunds));
})
@ -935,16 +928,16 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |keyed_account| {
with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| {
let state = AccountUtilsState::<Versions>::state(keyed_account)
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
let mut signers = HashSet::new();
signers.insert(*keyed_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let authority = *keyed_account.unsigned_key();
let result = keyed_account.initialize_nonce_account(&authority, &rent, &invoke_context);
let result = keyed_account.initialize_nonce_account(&authority, &rent, invoke_context);
let data = nonce::state::Data::new(
authority,
invoke_context.blockhash,
@ -965,15 +958,14 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |keyed_account| {
let invoke_context = create_invoke_context_with_blockhash(31);
with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| {
set_invoke_context_blockhash(invoke_context, 31);
let authorized = *keyed_account.unsigned_key();
keyed_account
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
let invoke_context = create_invoke_context_with_blockhash(0);
let result =
keyed_account.initialize_nonce_account(&authorized, &rent, &invoke_context);
set_invoke_context_blockhash(invoke_context, 0);
let result = keyed_account.initialize_nonce_account(&authorized, &rent, invoke_context);
assert_eq!(result, Err(InstructionError::InvalidAccountData));
})
}
@ -985,11 +977,10 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports - 42, true, |keyed_account| {
let invoke_context = create_invoke_context_with_blockhash(63);
with_mockup(min_lamports - 42, true, |invoke_context, keyed_account| {
set_invoke_context_blockhash(invoke_context, 63);
let authorized = *keyed_account.unsigned_key();
let result =
keyed_account.initialize_nonce_account(&authorized, &rent, &invoke_context);
let result = keyed_account.initialize_nonce_account(&authorized, &rent, invoke_context);
assert_eq!(result, Err(InstructionError::InsufficientFunds));
})
}
@ -1001,13 +992,13 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
let mut signers = HashSet::new();
signers.insert(*nonce_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(31);
set_invoke_context_blockhash(invoke_context, 31);
let authorized = *nonce_account.unsigned_key();
nonce_account
.initialize_nonce_account(&authorized, &rent, &invoke_context)
.initialize_nonce_account(&authorized, &rent, invoke_context)
.unwrap();
let authority = Pubkey::default();
let data = nonce::state::Data::new(
@ -1015,11 +1006,8 @@ mod test {
invoke_context.blockhash,
invoke_context.lamports_per_signature,
);
let result = nonce_account.authorize_nonce_account(
&Pubkey::default(),
&signers,
&invoke_context,
);
let result =
nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context);
assert_eq!(result, Ok(()));
let state = AccountUtilsState::<Versions>::state(nonce_account)
.unwrap()
@ -1035,14 +1023,11 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
let mut signers = HashSet::new();
signers.insert(*nonce_account.signer_key().unwrap());
let result = nonce_account.authorize_nonce_account(
&Pubkey::default(),
&signers,
&InvokeContext::new_mock(&[], &[]),
);
let result =
nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context);
assert_eq!(result, Err(InstructionError::InvalidAccountData));
})
}
@ -1054,35 +1039,32 @@ mod test {
..Rent::default()
};
let min_lamports = rent.minimum_balance(State::size());
with_test_keyed_account(min_lamports + 42, true, |nonce_account| {
with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| {
let mut signers = HashSet::new();
signers.insert(*nonce_account.signer_key().unwrap());
let invoke_context = create_invoke_context_with_blockhash(31);
set_invoke_context_blockhash(invoke_context, 31);
let authorized = &Pubkey::default().clone();
nonce_account
.initialize_nonce_account(authorized, &rent, &invoke_context)
.initialize_nonce_account(authorized, &rent, invoke_context)
.unwrap();
let result = nonce_account.authorize_nonce_account(
&Pubkey::default(),
&signers,
&invoke_context,
);
let result =
nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context);
assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
})
}
#[test]
fn verify_nonce_ok() {
with_test_keyed_account(42, true, |nonce_account| {
with_mockup(42, true, |invoke_context, nonce_account| {
let mut signers = HashSet::new();
signers.insert(nonce_account.signer_key().unwrap());
let state: State = nonce_account.state().unwrap();
// New is in Uninitialzed state
assert_eq!(state, State::Uninitialized);
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let authorized = nonce_account.unsigned_key();
nonce_account
.initialize_nonce_account(authorized, &Rent::free(), &invoke_context)
.initialize_nonce_account(authorized, &Rent::free(), invoke_context)
.unwrap();
assert!(verify_nonce_account(
&nonce_account.account.borrow(),
@ -1093,7 +1075,7 @@ mod test {
#[test]
fn verify_nonce_bad_acc_state_fail() {
with_test_keyed_account(42, true, |nonce_account| {
with_mockup(42, true, |_invoke_context, nonce_account| {
assert!(!verify_nonce_account(
&nonce_account.account.borrow(),
&Hash::default()
@ -1103,18 +1085,18 @@ mod test {
#[test]
fn verify_nonce_bad_query_hash_fail() {
with_test_keyed_account(42, true, |nonce_account| {
with_mockup(42, true, |invoke_context, nonce_account| {
let mut signers = HashSet::new();
signers.insert(nonce_account.signer_key().unwrap());
let state: State = nonce_account.state().unwrap();
// New is in Uninitialzed state
assert_eq!(state, State::Uninitialized);
let invoke_context = create_invoke_context_with_blockhash(0);
set_invoke_context_blockhash(invoke_context, 0);
let authorized = nonce_account.unsigned_key();
nonce_account
.initialize_nonce_account(authorized, &Rent::free(), &invoke_context)
.initialize_nonce_account(authorized, &Rent::free(), invoke_context)
.unwrap();
let invoke_context = create_invoke_context_with_blockhash(1);
set_invoke_context_blockhash(invoke_context, 1);
assert!(!verify_nonce_account(
&nonce_account.account.borrow(),
&invoke_context.blockhash,

View File

@ -502,6 +502,7 @@ mod tests {
system_instruction, system_program, sysvar,
sysvar::recent_blockhashes::IterItem,
transaction::TransactionError,
transaction_context::TransactionContext,
};
use {
super::*,
@ -674,7 +675,8 @@ mod tests {
#[test]
fn test_address_create_with_seed_mismatch() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let from = Pubkey::new_unique();
let seed = "dull boy";
let to = Pubkey::new_unique();
@ -688,7 +690,8 @@ mod tests {
#[test]
fn test_create_account_with_seed_missing_sig() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
let seed = "dull boy";
@ -718,7 +721,8 @@ mod tests {
#[test]
fn test_create_with_zero_lamports() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// create account with zero lamports transferred
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -752,7 +756,8 @@ mod tests {
#[test]
fn test_create_negative_lamports() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Attempt to create account with more lamports than remaining in from_account
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -776,7 +781,8 @@ mod tests {
#[test]
fn test_request_more_than_allowed_data_length() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
let from = Pubkey::new_unique();
let to_account = RefCell::new(AccountSharedData::new(0, 0, &system_program::id()));
@ -823,7 +829,8 @@ mod tests {
#[test]
fn test_create_already_in_use() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Attempt to create system account in account already owned by another program
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -891,7 +898,8 @@ mod tests {
#[test]
fn test_create_unsigned() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Attempt to create an account without signing the transfer
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -946,7 +954,8 @@ mod tests {
#[test]
fn test_create_sysvar_invalid_id_with_feature() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Attempt to create system account in account already owned by another program
let from = Pubkey::new_unique();
let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id()));
@ -980,7 +989,8 @@ mod tests {
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let mut invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
// Attempt to create system account in account already owned by another program
let from = Pubkey::new_unique();
@ -1007,7 +1017,8 @@ mod tests {
#[test]
fn test_create_data_populated() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
// Attempt to create system account in account with populated data
let new_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_unique();
@ -1040,7 +1051,8 @@ mod tests {
#[test]
fn test_create_from_account_is_nonce_fail() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let nonce = Pubkey::new_unique();
let nonce_account = RefCell::new(
AccountSharedData::new_data(
@ -1078,7 +1090,8 @@ mod tests {
#[test]
fn test_assign() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let new_owner = Pubkey::new(&[9; 32]);
let pubkey = Pubkey::new_unique();
let mut account = AccountSharedData::new(100, 0, &system_program::id());
@ -1120,7 +1133,8 @@ mod tests {
#[test]
fn test_assign_to_sysvar_with_feature() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let new_owner = sysvar::id();
let from = Pubkey::new_unique();
let mut from_account = AccountSharedData::new(100, 0, &system_program::id());
@ -1146,7 +1160,8 @@ mod tests {
feature_set
.inactive
.insert(feature_set::rent_for_sysvars::id());
let mut invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
invoke_context.feature_set = Arc::new(feature_set);
let new_owner = sysvar::id();
let from = Pubkey::new_unique();
@ -1197,7 +1212,8 @@ mod tests {
#[test]
fn test_transfer_lamports() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let from = Pubkey::new_unique();
let from_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
let to = Pubkey::new(&[3; 32]);
@ -1235,7 +1251,8 @@ mod tests {
#[test]
fn test_transfer_with_seed() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let base = Pubkey::new_unique();
let base_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter
let from_base_keyed_account = KeyedAccount::new(&base, true, &base_account);
@ -1295,7 +1312,8 @@ mod tests {
#[test]
fn test_transfer_lamports_from_nonce_account_fail() {
let invoke_context = InvokeContext::new_mock(&[], &[]);
let transaction_context = TransactionContext::new(Vec::new(), 1);
let invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let from = Pubkey::new_unique();
let from_account = RefCell::new(
AccountSharedData::new_data(

View File

@ -46,6 +46,7 @@ pub mod signer;
pub mod system_transaction;
pub mod timing;
pub mod transaction;
pub mod transaction_context;
pub mod transport;
pub mod wasm;

View File

@ -0,0 +1,342 @@
//! Successors of instruction_context_context::StackFrame, KeyedAccount and AccountInfo
use crate::{
account::{AccountSharedData, ReadableAccount, WritableAccount},
instruction::InstructionError,
pubkey::Pubkey,
};
use std::cell::{RefCell, RefMut};
pub type TransactionAccount = (Pubkey, AccountSharedData);
#[derive(Clone, Debug)]
pub struct InstructionAccount {
pub index: usize,
pub is_signer: bool,
pub is_writable: bool,
}
/// Loaded transaction shared between runtime and programs.
///
/// This context is valid for the entire duration of a transaction being processed.
pub struct TransactionContext {
account_keys: Vec<Pubkey>,
accounts: Vec<RefCell<AccountSharedData>>,
instruction_context_capacity: usize,
instruction_context_stack: Vec<InstructionContext>,
return_data: (Pubkey, Vec<u8>),
}
impl TransactionContext {
/// Constructs a new TransactionContext
pub fn new(
transaction_accounts: Vec<TransactionAccount>,
instruction_context_capacity: usize,
) -> Self {
let (account_keys, accounts) = transaction_accounts
.into_iter()
.map(|(key, account)| (key, RefCell::new(account)))
.unzip();
Self {
account_keys,
accounts,
instruction_context_capacity,
instruction_context_stack: Vec::with_capacity(instruction_context_capacity),
return_data: (Pubkey::default(), Vec::new()),
}
}
/// Used by the bank in the runtime to write back the processed accounts
pub fn deconstruct(self) -> Vec<TransactionAccount> {
self.account_keys
.into_iter()
.zip(
self.accounts
.into_iter()
.map(|account| account.into_inner()),
)
.collect()
}
/// Used in mock_process_instruction
pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
if !self.instruction_context_stack.is_empty() {
return Err(InstructionError::CallDepth);
}
Ok(self
.accounts
.into_iter()
.map(|account| account.into_inner())
.collect())
}
/// Returns the total number of accounts loaded in this Transaction
pub fn get_number_of_accounts(&self) -> usize {
self.accounts.len()
}
/// Searches for an account by its key
pub fn get_key_of_account_at_index(&self, index_in_transaction: usize) -> &Pubkey {
&self.account_keys[index_in_transaction]
}
/// Searches for an account by its key
pub fn get_account_at_index(&self, index_in_transaction: usize) -> &RefCell<AccountSharedData> {
&self.accounts[index_in_transaction]
}
/// Searches for an account by its key
pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<usize> {
self.account_keys.iter().position(|key| key == pubkey)
}
/// Searches for a program account by its key
pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<usize> {
self.account_keys.iter().rposition(|key| key == pubkey)
}
/// Gets an InstructionContext by its height in the stack
pub fn get_instruction_context_at(
&self,
instruction_context_height: usize,
) -> Result<&InstructionContext, InstructionError> {
if instruction_context_height >= self.instruction_context_stack.len() {
return Err(InstructionError::CallDepth);
}
Ok(&self.instruction_context_stack[instruction_context_height])
}
/// Gets the max height of the InstructionContext stack
pub fn get_instruction_context_capacity(&self) -> usize {
self.instruction_context_capacity
}
/// Gets the height of the current InstructionContext
pub fn get_instruction_context_height(&self) -> usize {
self.instruction_context_stack.len().saturating_sub(1)
}
/// Returns the current InstructionContext
pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
self.get_instruction_context_at(self.get_instruction_context_height())
}
/// Gets the last program account of the current InstructionContext
pub fn try_borrow_program_account(&self) -> Result<BorrowedAccount, InstructionError> {
let instruction_context = self.get_current_instruction_context()?;
instruction_context.try_borrow_account(
self,
instruction_context
.number_of_program_accounts
.saturating_sub(1),
)
}
/// Gets an instruction account of the current InstructionContext
pub fn try_borrow_instruction_account(
&self,
index_in_instruction: usize,
) -> Result<BorrowedAccount, InstructionError> {
let instruction_context = self.get_current_instruction_context()?;
instruction_context.try_borrow_account(
self,
instruction_context
.number_of_program_accounts
.saturating_add(index_in_instruction),
)
}
/// Pushes a new InstructionContext
pub fn push(
&mut self,
number_of_program_accounts: usize,
instruction_accounts: &[InstructionAccount],
instruction_data: Vec<u8>,
) -> Result<(), InstructionError> {
if self.instruction_context_stack.len() >= self.instruction_context_capacity {
return Err(InstructionError::CallDepth);
}
let mut result = InstructionContext {
instruction_data,
number_of_program_accounts,
account_indices: Vec::with_capacity(instruction_accounts.len()),
account_is_signer: Vec::with_capacity(instruction_accounts.len()),
account_is_writable: Vec::with_capacity(instruction_accounts.len()),
};
for instruction_account in instruction_accounts.iter() {
result.account_indices.push(instruction_account.index);
result.account_is_signer.push(instruction_account.is_signer);
result
.account_is_writable
.push(instruction_account.is_writable);
}
self.instruction_context_stack.push(result);
Ok(())
}
/// Pops the current InstructionContext
pub fn pop(&mut self) -> Result<(), InstructionError> {
if self.instruction_context_stack.is_empty() {
return Err(InstructionError::CallDepth);
}
self.instruction_context_stack.pop();
Ok(())
}
/// Gets the return data of the current InstructionContext or any above
pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
(&self.return_data.0, &self.return_data.1)
}
/// Set the return data of the current InstructionContext
pub fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
let pubkey = *self.try_borrow_program_account()?.get_key();
self.return_data = (pubkey, data);
Ok(())
}
}
/// Loaded instruction shared between runtime and programs.
///
/// This context is valid for the entire duration of a (possibly cross program) instruction being processed.
pub struct InstructionContext {
number_of_program_accounts: usize,
account_indices: Vec<usize>,
account_is_signer: Vec<bool>,
account_is_writable: Vec<bool>,
instruction_data: Vec<u8>,
}
impl InstructionContext {
/// Number of program accounts
pub fn get_number_of_program_accounts(&self) -> usize {
self.number_of_program_accounts
}
/// Number of accounts in this Instruction (without program accounts)
pub fn get_number_of_instruction_accounts(&self) -> usize {
self.account_indices
.len()
.saturating_sub(self.number_of_program_accounts)
}
/// Total number of accounts in this Instruction (with program accounts)
pub fn get_total_number_of_accounts(&self) -> usize {
self.account_indices.len()
}
/// Data parameter for the programs `process_instruction` handler
pub fn get_instruction_data(&self) -> &[u8] {
&self.instruction_data
}
/// Tries to borrow an account from this Instruction
pub fn try_borrow_account<'a, 'b: 'a>(
&'a self,
transaction_context: &'b TransactionContext,
index_in_instruction: usize,
) -> Result<BorrowedAccount<'a>, InstructionError> {
if index_in_instruction >= self.account_indices.len() {
return Err(InstructionError::NotEnoughAccountKeys);
}
let index_in_transaction = self.account_indices[index_in_instruction];
if index_in_transaction >= transaction_context.accounts.len() {
return Err(InstructionError::MissingAccount);
}
let account = transaction_context.accounts[index_in_transaction]
.try_borrow_mut()
.map_err(|_| InstructionError::AccountBorrowFailed)?;
Ok(BorrowedAccount {
transaction_context,
instruction_context: self,
index_in_transaction,
index_in_instruction,
account,
})
}
}
/// Shared account borrowed from the TransactionContext and an InstructionContext.
pub struct BorrowedAccount<'a> {
transaction_context: &'a TransactionContext,
instruction_context: &'a InstructionContext,
index_in_transaction: usize,
index_in_instruction: usize,
account: RefMut<'a, AccountSharedData>,
}
impl<'a> BorrowedAccount<'a> {
/// Returns the public key of this account (transaction wide)
pub fn get_key(&self) -> &Pubkey {
&self.transaction_context.account_keys[self.index_in_transaction]
}
/// Returns the owner of this account (transaction wide)
pub fn get_owner(&self) -> &Pubkey {
self.account.owner()
}
/// Assignes the owner of this account (transaction wide)
pub fn set_owner(&mut self, pubkey: Pubkey) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
self.account.set_owner(pubkey);
Ok(())
}
/// Returns the number of lamports of this account (transaction wide)
pub fn get_lamports(&self) -> u64 {
self.account.lamports()
}
/// Overwrites the number of lamports of this account (transaction wide)
pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
self.account.set_lamports(lamports);
Ok(())
}
/// Returns a read-only slice of the account data (transaction wide)
pub fn get_data(&self) -> &[u8] {
self.account.data()
}
/// Returns a writable slice of the account data (transaction wide)
pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
Ok(self.account.data_as_mut_slice())
}
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
// TODO
}*/
/// Returns whether this account is executable (transaction wide)
pub fn is_executable(&self) -> bool {
self.account.executable()
}
/// Configures whether this account is executable (transaction wide)
pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
if !self.is_writable() {
return Err(InstructionError::Immutable);
}
self.account.set_executable(is_executable);
Ok(())
}
/// Returns whether this account is a signer (instruction wide)
pub fn is_signer(&self) -> bool {
self.instruction_context.account_is_signer[self.index_in_instruction]
}
/// Returns whether this account is writable (instruction wide)
pub fn is_writable(&self) -> bool {
self.instruction_context.account_is_writable[self.index_in_instruction]
}
}