Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Refactor: Remove KeyedAccount from program runtime (#22226)
Browse files Browse the repository at this point in the history
* Makes error handling in BorrowedAccount optional.
Adds BorrowedAccount ::get_rent_epoch().
Exposes InstructionContext::get_index_in_transaction().
Turns accounts and account_keys into pinned boxed slices.

* Introduces "unsafe" to InvokeContext::push().

* Turns &TransactionContext into &mut TransactionContext in InvokeContext.

* Push and pop InstructionContext in InvokeContext.
Makes test_process_cross_program and test_native_invoke symmetric.
Removes the borrow check from test_invoke_context_verify.

* Removes keyed_accounts from prepare_instruction()

* Removes usage of invoke_stack.

* Removes keyed_accounts from program-test.

* Removes caller_write_privileges.

* Removes keyed_accounts from BPF parameter (de-)serialization.
  • Loading branch information
Lichtso authored Jan 3, 2022
1 parent 672fed0 commit 73e6038
Show file tree
Hide file tree
Showing 18 changed files with 841 additions and 799 deletions.
4 changes: 2 additions & 2 deletions cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,8 +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 transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
let mut transaction_context = TransactionContext::new(Vec::new(), 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);

// Verify the program
Executable::<BpfError, ThisInstructionMeter>::from_elf(
Expand Down
372 changes: 181 additions & 191 deletions program-runtime/src/invoke_context.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion program-runtime/src/native_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl NativeLoader {
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let (program_id, name_vec) = {
let program_id = invoke_context.get_caller()?;
let program_id = invoke_context.transaction_context.get_program_key()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
if native_loader::id() != *program_id {
Expand Down
101 changes: 59 additions & 42 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use {
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
std::{
cell::RefCell,
collections::HashMap,
collections::HashSet,
convert::TryFrom,
fs::File,
io::{self, Read},
Expand Down Expand Up @@ -100,57 +100,68 @@ pub fn builtin_process_instruction(
) -> Result<(), InstructionError> {
set_invoke_context(invoke_context);

let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let indices_in_instruction = instruction_context.get_number_of_program_accounts()
..instruction_context.get_number_of_accounts();

let log_collector = invoke_context.get_log_collector();
let program_id = invoke_context.get_caller()?;
let program_id = transaction_context.get_program_key()?;
stable_log::program_invoke(&log_collector, program_id, invoke_context.invoke_depth());

// Skip the processor account
let keyed_accounts = &invoke_context.get_keyed_accounts()?[1..];
// Copy indices_in_instruction into a HashSet to ensure there are no duplicates
let deduplicated_indices: HashSet<usize> = indices_in_instruction.clone().collect();

// Copy all the accounts into a HashMap to ensure there are no duplicates
let mut accounts: HashMap<Pubkey, Account> = keyed_accounts
// Create copies of the accounts
let mut account_copies = deduplicated_indices
.iter()
.map(|ka| {
(
*ka.unsigned_key(),
Account::from(ka.account.borrow().clone()),
)
.map(|index_in_instruction| {
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, *index_in_instruction)?;
Ok((
*borrowed_account.get_key(),
*borrowed_account.get_owner(),
borrowed_account.get_lamports(),
borrowed_account.get_data().to_vec(),
))
})
.collect();
.collect::<Result<Vec<_>, InstructionError>>()?;

// Create shared references to each account's lamports/data/owner
let account_refs: HashMap<_, _> = accounts
// Create shared references to account_copies
let account_refs: Vec<_> = account_copies
.iter_mut()
.map(|(key, account)| {
.map(|(key, owner, lamports, data)| {
(
*key,
(
Rc::new(RefCell::new(&mut account.lamports)),
Rc::new(RefCell::new(&mut account.data[..])),
&account.owner,
),
key,
owner,
Rc::new(RefCell::new(lamports)),
Rc::new(RefCell::new(data.as_mut())),
)
})
.collect();

// Create AccountInfos
let account_infos: Vec<AccountInfo> = keyed_accounts
.iter()
.map(|keyed_account| {
let key = keyed_account.unsigned_key();
let (lamports, data, owner) = &account_refs[key];
AccountInfo {
let account_infos = indices_in_instruction
.map(|index_in_instruction| {
let account_copy_index = deduplicated_indices
.iter()
.position(|index| *index == index_in_instruction)
.unwrap();
let (key, owner, lamports, data) = &account_refs[account_copy_index];
let borrowed_account = instruction_context
.try_borrow_account(transaction_context, index_in_instruction)?;
Ok(AccountInfo {
key,
is_signer: keyed_account.signer_key().is_some(),
is_writable: keyed_account.is_writable(),
is_signer: borrowed_account.is_signer(),
is_writable: borrowed_account.is_writable(),
lamports: lamports.clone(),
data: data.clone(),
owner,
executable: keyed_account.executable().unwrap(),
rent_epoch: keyed_account.rent_epoch().unwrap(),
}
executable: borrowed_account.is_executable(),
rent_epoch: borrowed_account.get_rent_epoch(),
})
})
.collect();
.collect::<Result<Vec<AccountInfo>, InstructionError>>()?;

// Execute the program
process_instruction(program_id, &account_infos, input).map_err(|err| {
Expand All @@ -161,12 +172,16 @@ pub fn builtin_process_instruction(
stable_log::program_success(&log_collector, program_id);

// Commit AccountInfo changes back into KeyedAccounts
for keyed_account in keyed_accounts {
let mut account = keyed_account.account.borrow_mut();
let key = keyed_account.unsigned_key();
let (lamports, data, _owner) = &account_refs[key];
account.set_lamports(**lamports.borrow());
account.set_data(data.borrow().to_vec());
for (index_in_instruction, (_key, _owner, lamports, data)) in deduplicated_indices
.into_iter()
.zip(account_copies.into_iter())
{
let mut borrowed_account =
instruction_context.try_borrow_account(transaction_context, index_in_instruction)?;
if borrowed_account.is_writable() {
borrowed_account.set_lamports(lamports)?;
borrowed_account.set_data(&data)?;
}
}

Ok(())
Expand Down Expand Up @@ -233,7 +248,10 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
let invoke_context = get_invoke_context();
let log_collector = invoke_context.get_log_collector();

let caller = *invoke_context.get_caller().expect("get_caller");
let caller = *invoke_context
.transaction_context
.get_program_key()
.unwrap();

stable_log::program_invoke(
&log_collector,
Expand All @@ -245,7 +263,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
.iter()
.map(|seeds| Pubkey::create_program_address(seeds, &caller).unwrap())
.collect::<Vec<_>>();
let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context
let (instruction_accounts, program_indices) = invoke_context
.prepare_instruction(instruction, &signers)
.unwrap();

Expand Down Expand Up @@ -281,7 +299,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
.process_instruction(
&instruction.data,
&instruction_accounts,
Some(&caller_write_privileges),
&program_indices,
&mut compute_units_consumed,
)
Expand Down
14 changes: 4 additions & 10 deletions programs/bpf/benches/bpf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,9 @@ fn bench_create_vm(bencher: &mut Bencher) {
.mock_set_remaining(BUDGET);

// Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();

Expand Down Expand Up @@ -250,12 +247,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
.mock_set_remaining(BUDGET);

// Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (mut serialized, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();

Expand Down
14 changes: 5 additions & 9 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,9 @@ fn run_program(name: &str) -> u64 {
file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id();
with_mock_invoke_context(loader_id, 0, |invoke_context| {
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let (parameter_bytes, account_lengths) = serialize_parameters(
&keyed_accounts[0].unsigned_key(),
&keyed_accounts[1].unsigned_key(),
&keyed_accounts[2..],
&[],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
)
.unwrap();

Expand All @@ -227,7 +224,7 @@ fn run_program(name: &str) -> u64 {
let mut instruction_count = 0;
let mut tracer = None;
for i in 0..2 {
invoke_context.return_data = (*invoke_context.get_caller().unwrap(), Vec::new());
invoke_context.return_data = (*invoke_context.transaction_context.get_program_key().unwrap(), Vec::new());
let mut parameter_bytes = parameter_bytes.clone();
{
let mut vm = create_vm(
Expand Down Expand Up @@ -278,10 +275,9 @@ fn run_program(name: &str) -> u64 {
tracer = Some(vm.get_tracer().clone());
}
}
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
deserialize_parameters(
&loader_id,
&keyed_accounts[2..],
invoke_context.transaction_context,
invoke_context.transaction_context.get_current_instruction_context().unwrap(),
parameter_bytes.as_slice(),
&account_lengths,
true,
Expand Down
Loading

0 comments on commit 73e6038

Please sign in to comment.