CPI Account Reuse (#19762)

* Removes two account copy steps from InstructionProcessor::native_invoke().

* Moves gathering of keyed_accounts, caller_write_privileges and program_indices into InstructionProcessor::create_message().

* Explicitly routes the serialized account lengths to enable sharing of existing account structures.

* Recycles existing account structs in CPI syscall.
This commit is contained in:
Alexander Meißner 2021-09-18 08:09:47 +02:00 committed by GitHub
parent efd024510a
commit 36f46e1c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 352 additions and 550 deletions

View File

@ -7,7 +7,6 @@ use solana_sdk::{
feature_set::{demote_program_write_locks, fix_write_privs}, feature_set::{demote_program_write_locks, fix_write_privs},
ic_logger_msg, ic_msg, ic_logger_msg, ic_msg,
instruction::{CompiledInstruction, Instruction, InstructionError}, instruction::{CompiledInstruction, Instruction, InstructionError},
keyed_account::{keyed_account_at_index, KeyedAccount},
message::Message, message::Message,
process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext}, process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext},
pubkey::Pubkey, pubkey::Pubkey,
@ -15,7 +14,7 @@ use solana_sdk::{
system_program, system_program,
}; };
use std::{ use std::{
cell::{Ref, RefCell}, cell::{Ref, RefCell, RefMut},
collections::HashMap, collections::HashMap,
rc::Rc, rc::Rc,
sync::Arc, sync::Arc,
@ -375,13 +374,34 @@ impl InstructionProcessor {
pub fn create_message( pub fn create_message(
instruction: &Instruction, instruction: &Instruction,
keyed_accounts: &[&KeyedAccount],
signers: &[Pubkey], signers: &[Pubkey],
invoke_context: &Ref<&mut dyn InvokeContext>, invoke_context: &RefMut<&mut dyn InvokeContext>,
) -> Result<(Message, Pubkey, usize), InstructionError> { ) -> Result<(Message, Vec<bool>, Vec<usize>), InstructionError> {
let message = Message::new(&[instruction.clone()], None);
// Gather keyed_accounts in the order of message.account_keys
let caller_keyed_accounts = invoke_context.get_keyed_accounts()?;
let callee_keyed_accounts = message
.account_keys
.iter()
.map(|account_key| {
caller_keyed_accounts
.iter()
.find(|keyed_account| keyed_account.unsigned_key() == account_key)
.ok_or_else(|| {
ic_msg!(
*invoke_context,
"Instruction references an unknown account {}",
account_key
);
InstructionError::MissingAccount
})
})
.collect::<Result<Vec<_>, InstructionError>>()?;
// Check for privilege escalation // Check for privilege escalation
for account in instruction.accounts.iter() { for account in instruction.accounts.iter() {
let keyed_account = keyed_accounts let keyed_account = callee_keyed_accounts
.iter() .iter()
.find_map(|keyed_account| { .find_map(|keyed_account| {
if &account.pubkey == keyed_account.unsigned_key() { if &account.pubkey == keyed_account.unsigned_key() {
@ -421,215 +441,118 @@ impl InstructionProcessor {
return Err(InstructionError::PrivilegeEscalation); return Err(InstructionError::PrivilegeEscalation);
} }
} }
let caller_write_privileges = callee_keyed_accounts
// validate the caller has access to the program account and that it is executable
let program_id = instruction.program_id;
match keyed_accounts
.iter() .iter()
.find(|keyed_account| &program_id == keyed_account.unsigned_key()) .map(|keyed_account| keyed_account.is_writable())
{ .collect::<Vec<bool>>();
Some(keyed_account) => {
if !keyed_account.executable()? { // Find and validate executables / program accounts
let callee_program_id = instruction.program_id;
let (program_account_index, program_account) = callee_keyed_accounts
.iter()
.find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
.and_then(|_keyed_account| invoke_context.get_account(&callee_program_id))
.ok_or_else(|| {
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
InstructionError::MissingAccount
})?;
if !program_account.borrow().executable() {
ic_msg!(
invoke_context,
"Account {} is not executable",
callee_program_id
);
return Err(InstructionError::AccountNotExecutable);
}
let mut program_indices = vec![program_account_index];
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program_account.borrow().state()?
{
if let Some((programdata_account_index, _programdata_account)) =
invoke_context.get_account(&programdata_address)
{
program_indices.push(programdata_account_index);
} else {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Account {} is not executable", "Unknown upgradeable programdata account {}",
keyed_account.unsigned_key() programdata_address,
); );
return Err(InstructionError::AccountNotExecutable); return Err(InstructionError::MissingAccount);
} }
} } else {
None => { ic_msg!(
ic_msg!(invoke_context, "Unknown program {}", program_id); invoke_context,
"Invalid upgradeable program account {}",
callee_program_id,
);
return Err(InstructionError::MissingAccount); return Err(InstructionError::MissingAccount);
} }
} }
let message = Message::new(&[instruction.clone()], None); Ok((message, caller_write_privileges, program_indices))
let program_id_index = message.instructions[0].program_id_index as usize;
Ok((message, program_id, program_id_index))
} }
/// Entrypoint for a cross-program invocation from a native program /// Entrypoint for a cross-program invocation from a native program
pub fn native_invoke( pub fn native_invoke(
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
instruction: Instruction, instruction: Instruction,
keyed_account_indices: &[usize], keyed_account_indices_obsolete: &[usize],
signers: &[Pubkey], signers: &[Pubkey],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let invoke_context = RefCell::new(invoke_context); let invoke_context = RefCell::new(invoke_context);
let mut invoke_context = invoke_context.borrow_mut();
let ( // Translate and verify caller's data
message, let (message, mut caller_write_privileges, program_indices) =
program_indices, Self::create_message(&instruction, signers, &invoke_context)?;
accounts, if !invoke_context.is_feature_active(&fix_write_privs::id()) {
keyed_account_indices_reordered,
caller_write_privileges,
) = {
let invoke_context = invoke_context.borrow();
let caller_keyed_accounts = invoke_context.get_keyed_accounts()?; let caller_keyed_accounts = invoke_context.get_keyed_accounts()?;
let callee_keyed_accounts = keyed_account_indices caller_write_privileges = Vec::with_capacity(1 + keyed_account_indices_obsolete.len());
.iter() caller_write_privileges.push(false);
.map(|index| keyed_account_at_index(caller_keyed_accounts, *index)) for index in keyed_account_indices_obsolete.iter() {
.collect::<Result<Vec<&KeyedAccount>, InstructionError>>()?; caller_write_privileges.push(caller_keyed_accounts[*index].is_writable());
let (message, callee_program_id, _) = Self::create_message(
&instruction,
&callee_keyed_accounts,
signers,
&invoke_context,
)?;
let mut keyed_account_indices_reordered =
Vec::with_capacity(message.account_keys.len());
let mut accounts = Vec::with_capacity(message.account_keys.len());
let mut caller_write_privileges = Vec::with_capacity(message.account_keys.len());
// Translate and verify caller's data
if invoke_context.is_feature_active(&fix_write_privs::id()) {
'root: for account_key in message.account_keys.iter() {
for keyed_account_index in keyed_account_indices {
let keyed_account = &caller_keyed_accounts[*keyed_account_index];
if account_key == keyed_account.unsigned_key() {
accounts.push((*account_key, Rc::new(keyed_account.account.clone())));
caller_write_privileges.push(keyed_account.is_writable());
keyed_account_indices_reordered.push(*keyed_account_index);
continue 'root;
}
}
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
return Err(InstructionError::MissingAccount);
}
} else {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
for index in keyed_account_indices.iter() {
caller_write_privileges.push(keyed_accounts[*index].is_writable());
}
caller_write_privileges.insert(0, false);
let keyed_accounts = invoke_context.get_keyed_accounts()?;
'root2: for account_key in message.account_keys.iter() {
for keyed_account_index in keyed_account_indices {
let keyed_account = &keyed_accounts[*keyed_account_index];
if account_key == keyed_account.unsigned_key() {
accounts.push((*account_key, Rc::new(keyed_account.account.clone())));
keyed_account_indices_reordered.push(*keyed_account_index);
continue 'root2;
}
}
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
return Err(InstructionError::MissingAccount);
}
} }
// Process instruction
invoke_context.record_instruction(&instruction);
let (program_account_index, program_account) = invoke_context
.get_account(&callee_program_id)
.ok_or_else(|| {
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
InstructionError::MissingAccount
})?;
if !program_account.borrow().executable() {
ic_msg!(
invoke_context,
"Account {} is not executable",
callee_program_id
);
return Err(InstructionError::AccountNotExecutable);
}
let mut program_indices = vec![];
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program_account.borrow().state()?
{
if let Some((programdata_account_index, _programdata_account)) =
invoke_context.get_account(&programdata_address)
{
program_indices.push(programdata_account_index);
} else {
ic_msg!(
invoke_context,
"Unknown upgradeable programdata account {}",
programdata_address,
);
return Err(InstructionError::MissingAccount);
}
} else {
ic_msg!(
invoke_context,
"Upgradeable program account state not valid {}",
callee_program_id,
);
return Err(InstructionError::MissingAccount);
}
}
program_indices.insert(0, program_account_index);
(
message,
program_indices,
accounts,
keyed_account_indices_reordered,
caller_write_privileges,
)
}; };
let accounts = message
.account_keys
.iter()
.map(|account_key| {
invoke_context
.get_account(account_key)
.ok_or(InstructionError::MissingAccount)
.map(|(_account_index, account)| (*account_key, account))
})
.collect::<Result<Vec<_>, InstructionError>>()?;
let account_sizes = accounts
.iter()
.map(|(_key, account)| account.borrow().data().len())
.collect::<Vec<_>>();
#[allow(clippy::deref_addrof)] // Record the instruction
invoke_context.record_instruction(&instruction);
// Process instruction
InstructionProcessor::process_cross_program_instruction( InstructionProcessor::process_cross_program_instruction(
&message, &message,
&program_indices, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
*(&mut *(invoke_context.borrow_mut())), *invoke_context,
)?; )?;
// Copy results back to caller // Verify the called program has not misbehaved
for ((_key, account), prev_size) in accounts.iter().zip(account_sizes.iter()) {
{ if *prev_size != account.borrow().data().len() && *prev_size != 0 {
let invoke_context = invoke_context.borrow(); // Only support for `CreateAccount` at this time.
let demote_program_write_locks = // Need a way to limit total realloc size across multiple CPI calls
invoke_context.is_feature_active(&demote_program_write_locks::id()); ic_msg!(
let keyed_accounts = invoke_context.get_keyed_accounts()?; invoke_context,
for (src_keyed_account_index, ((_key, account), dst_keyed_account_index)) in accounts "Inner instructions do not support realloc, only SystemProgram::CreateAccount",
.iter() );
.zip(keyed_account_indices_reordered) return Err(InstructionError::InvalidRealloc);
.enumerate()
{
let dst_keyed_account = &keyed_accounts[dst_keyed_account_index];
let src_keyed_account = account.borrow();
if message.is_writable(src_keyed_account_index, demote_program_write_locks)
&& !src_keyed_account.executable()
{
if dst_keyed_account.data_len()? != src_keyed_account.data().len()
&& dst_keyed_account.data_len()? != 0
{
// Only support for `CreateAccount` at this time.
// Need a way to limit total realloc size across multiple CPI calls
ic_msg!(
invoke_context,
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
);
return Err(InstructionError::InvalidRealloc);
}
dst_keyed_account
.try_account_ref_mut()?
.set_lamports(src_keyed_account.lamports());
dst_keyed_account
.try_account_ref_mut()?
.set_owner(*src_keyed_account.owner());
dst_keyed_account
.try_account_ref_mut()?
.set_data(src_keyed_account.data().to_vec());
}
} }
} }
@ -645,45 +568,46 @@ impl InstructionProcessor {
caller_write_privileges: &[bool], caller_write_privileges: &[bool],
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if let Some(instruction) = message.instructions.get(0) { // This function is always called with a valid instruction, if that changes return an error
let program_id = instruction.program_id(&message.account_keys); let instruction = message
.instructions
.get(0)
.ok_or(InstructionError::GenericError)?;
// Verify the calling program hasn't misbehaved let program_id = instruction.program_id(&message.account_keys);
invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?;
// clear the return data // Verify the calling program hasn't misbehaved
invoke_context.set_return_data(None); invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?;
// Invoke callee // clear the return data
invoke_context.push(program_id, message, instruction, program_indices, accounts)?; invoke_context.set_return_data(None);
let mut instruction_processor = InstructionProcessor::default(); // Invoke callee
for (program_id, process_instruction) in invoke_context.get_programs().iter() { invoke_context.push(program_id, message, instruction, program_indices, accounts)?;
instruction_processor.add_program(*program_id, *process_instruction);
}
let mut result = instruction_processor.process_instruction( let mut instruction_processor = InstructionProcessor::default();
program_id, for (program_id, process_instruction) in invoke_context.get_programs().iter() {
&instruction.data, instruction_processor.add_program(*program_id, *process_instruction);
invoke_context,
);
if result.is_ok() {
// Verify the called program has not misbehaved
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
let write_privileges: Vec<bool> = (0..message.account_keys.len())
.map(|i| message.is_writable(i, demote_program_write_locks))
.collect();
result = invoke_context.verify_and_update(instruction, accounts, &write_privileges);
}
// Restore previous state
invoke_context.pop();
result
} else {
// This function is always called with a valid instruction, if that changes return an error
Err(InstructionError::GenericError)
} }
let mut result = instruction_processor.process_instruction(
program_id,
&instruction.data,
invoke_context,
);
if result.is_ok() {
// Verify the called program has not misbehaved
let demote_program_write_locks =
invoke_context.is_feature_active(&demote_program_write_locks::id());
let write_privileges: Vec<bool> = (0..message.account_keys.len())
.map(|i| message.is_writable(i, demote_program_write_locks))
.collect();
result = invoke_context.verify_and_update(instruction, accounts, &write_privileges);
}
// Restore previous state
invoke_context.pop();
result
} }
/// Verify the results of a cross-program instruction /// Verify the results of a cross-program instruction

View File

@ -220,7 +220,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
// Serialize account data // Serialize account data
let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let keyed_accounts = invoke_context.get_keyed_accounts().unwrap();
let mut serialized = serialize_parameters( let (mut serialized, _account_lengths) = serialize_parameters(
&bpf_loader::id(), &bpf_loader::id(),
&solana_sdk::pubkey::new_rand(), &solana_sdk::pubkey::new_rand(),
keyed_accounts, keyed_accounts,

View File

@ -198,7 +198,7 @@ fn run_program(
let mut data = vec![]; let mut data = vec![];
file.read_to_end(&mut data).unwrap(); file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id(); let loader_id = bpf_loader::id();
let parameter_bytes = serialize_parameters( let (parameter_bytes, account_lengths) = serialize_parameters(
&bpf_loader::id(), &bpf_loader::id(),
program_id, program_id,
&parameter_accounts, &parameter_accounts,
@ -282,6 +282,7 @@ fn run_program(
&bpf_loader::id(), &bpf_loader::id(),
parameter_accounts, parameter_accounts,
parameter_bytes.as_slice(), parameter_bytes.as_slice(),
&account_lengths,
) )
.unwrap(); .unwrap();
} }

View File

@ -888,7 +888,7 @@ impl Executor for BpfExecutor {
let mut serialize_time = Measure::start("serialize"); let mut serialize_time = Measure::start("serialize");
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
let mut parameter_bytes = let (mut parameter_bytes, account_lengths) =
serialize_parameters(loader_id, program_id, keyed_accounts, instruction_data)?; serialize_parameters(loader_id, program_id, keyed_accounts, instruction_data)?;
serialize_time.stop(); serialize_time.stop();
let mut create_vm_time = Measure::start("create_vm"); let mut create_vm_time = Measure::start("create_vm");
@ -971,7 +971,12 @@ impl Executor for BpfExecutor {
} }
let mut deserialize_time = Measure::start("deserialize"); let mut deserialize_time = Measure::start("deserialize");
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
deserialize_parameters(loader_id, keyed_accounts, parameter_bytes.as_slice())?; deserialize_parameters(
loader_id,
keyed_accounts,
parameter_bytes.as_slice(),
&account_lengths,
)?;
deserialize_time.stop(); deserialize_time.stop();
invoke_context.update_timing( invoke_context.update_timing(
serialize_time.as_us(), serialize_time.as_us(),

View File

@ -28,23 +28,31 @@ pub fn serialize_parameters(
program_id: &Pubkey, program_id: &Pubkey,
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
data: &[u8], data: &[u8],
) -> Result<AlignedMemory, InstructionError> { ) -> Result<(AlignedMemory, Vec<usize>), InstructionError> {
if *loader_id == bpf_loader_deprecated::id() { if *loader_id == bpf_loader_deprecated::id() {
serialize_parameters_unaligned(program_id, keyed_accounts, data) serialize_parameters_unaligned(program_id, keyed_accounts, data)
} else { } else {
serialize_parameters_aligned(program_id, keyed_accounts, data) serialize_parameters_aligned(program_id, keyed_accounts, data)
} }
.and_then(|buffer| {
let account_lengths = keyed_accounts
.iter()
.map(|keyed_account| keyed_account.data_len())
.collect::<Result<Vec<usize>, InstructionError>>()?;
Ok((buffer, account_lengths))
})
} }
pub fn deserialize_parameters( pub fn deserialize_parameters(
loader_id: &Pubkey, loader_id: &Pubkey,
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
buffer: &[u8], buffer: &[u8],
account_lengths: &[usize],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if *loader_id == bpf_loader_deprecated::id() { if *loader_id == bpf_loader_deprecated::id() {
deserialize_parameters_unaligned(keyed_accounts, buffer) deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths)
} else { } else {
deserialize_parameters_aligned(keyed_accounts, buffer) deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths)
} }
} }
@ -126,9 +134,14 @@ pub fn serialize_parameters_unaligned(
pub fn deserialize_parameters_unaligned( pub fn deserialize_parameters_unaligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
buffer: &[u8], buffer: &[u8],
account_lengths: &[usize],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut start = size_of::<u64>(); // number of accounts let mut start = size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() { for (i, (keyed_account, _pre_len)) in keyed_accounts
.iter()
.zip(account_lengths.iter())
.enumerate()
{
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup start += 1; // is_dup
if !is_dup { if !is_dup {
@ -247,9 +260,14 @@ pub fn serialize_parameters_aligned(
pub fn deserialize_parameters_aligned( pub fn deserialize_parameters_aligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
buffer: &[u8], buffer: &[u8],
account_lengths: &[usize],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut start = size_of::<u64>(); // number of accounts let mut start = size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() { for (i, (keyed_account, pre_len)) in keyed_accounts
.iter()
.zip(account_lengths.iter())
.enumerate()
{
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += size_of::<u8>(); // position start += size_of::<u8>(); // position
if is_dup { if is_dup {
@ -265,18 +283,17 @@ pub fn deserialize_parameters_aligned(
start += size_of::<Pubkey>(); // owner start += size_of::<Pubkey>(); // owner
account.set_lamports(LittleEndian::read_u64(&buffer[start..])); account.set_lamports(LittleEndian::read_u64(&buffer[start..]));
start += size_of::<u64>(); // lamports start += size_of::<u64>(); // lamports
let pre_len = account.data().len();
let post_len = LittleEndian::read_u64(&buffer[start..]) as usize; let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
start += size_of::<u64>(); // data length start += size_of::<u64>(); // data length
let mut data_end = start + pre_len; let mut data_end = start + *pre_len;
if post_len != pre_len if post_len != *pre_len
&& (post_len.saturating_sub(pre_len)) <= MAX_PERMITTED_DATA_INCREASE && (post_len.saturating_sub(*pre_len)) <= MAX_PERMITTED_DATA_INCREASE
{ {
data_end = start + post_len; data_end = start + post_len;
} }
account.set_data_from_slice(&buffer[start..data_end]); account.set_data_from_slice(&buffer[start..data_end]);
start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data
start += (start as *const u8).align_offset(align_of::<u128>()); start += (start as *const u8).align_offset(align_of::<u128>());
start += size_of::<u64>(); // rent_epoch start += size_of::<u64>(); // rent_epoch
} }
@ -392,7 +409,7 @@ mod tests {
// check serialize_parameters_aligned // check serialize_parameters_aligned
let mut serialized = serialize_parameters( let (mut serialized, account_lengths) = serialize_parameters(
&bpf_loader::id(), &bpf_loader::id(),
&program_id, &program_id,
&keyed_accounts, &keyed_accounts,
@ -445,8 +462,13 @@ mod tests {
} }
}) })
.collect(); .collect();
deserialize_parameters(&bpf_loader::id(), &de_keyed_accounts, serialized.as_slice()) deserialize_parameters(
.unwrap(); &bpf_loader::id(),
&de_keyed_accounts,
serialized.as_slice(),
&account_lengths,
)
.unwrap();
for ((account, de_keyed_account), key) in for ((account, de_keyed_account), key) in
accounts.iter().zip(de_keyed_accounts).zip(keys.clone()) accounts.iter().zip(de_keyed_accounts).zip(keys.clone())
{ {
@ -458,7 +480,7 @@ mod tests {
// check serialize_parameters_unaligned // check serialize_parameters_unaligned
let mut serialized = serialize_parameters( let (mut serialized, account_lengths) = serialize_parameters(
&bpf_loader_deprecated::id(), &bpf_loader_deprecated::id(),
&program_id, &program_id,
&keyed_accounts, &keyed_accounts,
@ -497,6 +519,7 @@ mod tests {
&bpf_loader_deprecated::id(), &bpf_loader_deprecated::id(),
&de_keyed_accounts, &de_keyed_accounts,
serialized.as_slice(), serialized.as_slice(),
&account_lengths,
) )
.unwrap(); .unwrap();
for ((account, de_keyed_account), key) in for ((account, de_keyed_account), key) in

View File

@ -12,11 +12,9 @@ use solana_rbpf::{
#[allow(deprecated)] #[allow(deprecated)]
use solana_sdk::sysvar::fees::Fees; use solana_sdk::sysvar::fees::Fees;
use solana_sdk::{ use solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount}, account::{AccountSharedData, ReadableAccount, WritableAccount},
account_info::AccountInfo, account_info::AccountInfo,
account_utils::StateMut, blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
blake3, bpf_loader, bpf_loader_deprecated,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
clock::Clock, clock::Clock,
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
epoch_schedule::EpochSchedule, epoch_schedule::EpochSchedule,
@ -30,7 +28,7 @@ use solana_sdk::{
ic_msg, ic_msg,
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
keccak, keccak,
keyed_account::KeyedAccount, message::Message,
native_loader, native_loader,
process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger}, process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
program::MAX_RETURN_DATA, program::MAX_RETURN_DATA,
@ -1511,11 +1509,9 @@ struct AccountReferences<'a> {
vm_data_addr: u64, vm_data_addr: u64,
ref_to_len_in_vm: &'a mut u64, ref_to_len_in_vm: &'a mut u64,
serialized_len_ptr: &'a mut u64, serialized_len_ptr: &'a mut u64,
executable: bool,
rent_epoch: u64,
} }
type TranslatedAccount<'a> = (
Rc<RefCell<AccountSharedData>>,
Option<AccountReferences<'a>>,
);
type TranslatedAccounts<'a> = ( type TranslatedAccounts<'a> = (
Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>, Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
Vec<Option<AccountReferences<'a>>>, Vec<Option<AccountReferences<'a>>>,
@ -1529,14 +1525,15 @@ trait SyscallInvokeSigned<'a> {
&self, &self,
addr: u64, addr: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<Instruction, EbpfError<BpfError>>; ) -> Result<Instruction, EbpfError<BpfError>>;
fn translate_accounts( fn translate_accounts(
&self, &self,
account_keys: &[Pubkey], message: &Message,
program_account_index: usize,
account_infos_addr: u64, account_infos_addr: u64,
account_infos_len: u64, account_infos_len: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>>; ) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>>;
fn translate_signers( fn translate_signers(
&self, &self,
@ -1567,14 +1564,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
&self, &self,
addr: u64, addr: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<Instruction, EbpfError<BpfError>> { ) -> Result<Instruction, EbpfError<BpfError>> {
let ix = translate_type::<Instruction>(memory_mapping, addr, self.loader_id)?; let ix = translate_type::<Instruction>(memory_mapping, addr, self.loader_id)?;
check_instruction_size( check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?;
ix.accounts.len(),
ix.data.len(),
&self.invoke_context.borrow(),
)?;
let accounts = translate_slice::<AccountMeta>( let accounts = translate_slice::<AccountMeta>(
memory_mapping, memory_mapping,
@ -1599,21 +1593,19 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
fn translate_accounts( fn translate_accounts(
&self, &self,
account_keys: &[Pubkey], message: &Message,
program_account_index: usize,
account_infos_addr: u64, account_infos_addr: u64,
account_infos_len: u64, account_infos_len: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>> { ) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>> {
let invoke_context = self.invoke_context.borrow();
let account_infos = translate_slice::<AccountInfo>( let account_infos = translate_slice::<AccountInfo>(
memory_mapping, memory_mapping,
account_infos_addr, account_infos_addr,
account_infos_len, account_infos_len,
self.loader_id, self.loader_id,
)?; )?;
check_account_infos(account_infos.len(), &invoke_context)?; check_account_infos(account_infos.len(), invoke_context)?;
let account_info_keys = account_infos let account_info_keys = account_infos
.iter() .iter()
.map(|account_info| { .map(|account_info| {
@ -1625,8 +1617,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
}) })
.collect::<Result<Vec<_>, EbpfError<BpfError>>>()?; .collect::<Result<Vec<_>, EbpfError<BpfError>>>()?;
let translate = |account_info: &AccountInfo, let translate = |account_info: &AccountInfo, invoke_context: &mut dyn InvokeContext| {
invoke_context: &Ref<&mut dyn InvokeContext>| {
// Translate the account from user space // Translate the account from user space
let lamports = { let lamports = {
@ -1683,31 +1674,23 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
) )
}; };
Ok(( Ok(AccountReferences {
Rc::new(RefCell::new(AccountSharedData::from(Account { lamports,
lamports: *lamports, owner,
data: data.to_vec(), data,
executable: account_info.executable, vm_data_addr,
owner: *owner, ref_to_len_in_vm,
rent_epoch: account_info.rent_epoch, serialized_len_ptr,
}))), executable: account_info.executable,
Some(AccountReferences { rent_epoch: account_info.rent_epoch,
lamports, })
owner,
data,
vm_data_addr,
ref_to_len_in_vm,
serialized_len_ptr,
}),
))
}; };
get_translated_accounts( get_translated_accounts(
account_keys, message,
program_account_index,
&account_info_keys, &account_info_keys,
account_infos, account_infos,
&invoke_context, invoke_context,
translate, translate,
) )
} }
@ -1854,14 +1837,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
&self, &self,
addr: u64, addr: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<Instruction, EbpfError<BpfError>> { ) -> Result<Instruction, EbpfError<BpfError>> {
let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, self.loader_id)?; let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, self.loader_id)?;
check_instruction_size( check_instruction_size(ix_c.accounts_len, ix_c.data_len, invoke_context)?;
ix_c.accounts_len,
ix_c.data_len,
&self.invoke_context.borrow(),
)?;
let program_id = let program_id =
translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, self.loader_id)?; translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, self.loader_id)?;
let meta_cs = translate_slice::<SolAccountMeta>( let meta_cs = translate_slice::<SolAccountMeta>(
@ -1899,21 +1879,19 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
fn translate_accounts( fn translate_accounts(
&self, &self,
account_keys: &[Pubkey], message: &Message,
program_account_index: usize,
account_infos_addr: u64, account_infos_addr: u64,
account_infos_len: u64, account_infos_len: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: &mut dyn InvokeContext,
) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>> { ) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>> {
let invoke_context = self.invoke_context.borrow();
let account_infos = translate_slice::<SolAccountInfo>( let account_infos = translate_slice::<SolAccountInfo>(
memory_mapping, memory_mapping,
account_infos_addr, account_infos_addr,
account_infos_len, account_infos_len,
self.loader_id, self.loader_id,
)?; )?;
check_account_infos(account_infos.len(), &invoke_context)?; check_account_infos(account_infos.len(), invoke_context)?;
let account_info_keys = account_infos let account_info_keys = account_infos
.iter() .iter()
.map(|account_info| { .map(|account_info| {
@ -1921,8 +1899,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
}) })
.collect::<Result<Vec<_>, EbpfError<BpfError>>>()?; .collect::<Result<Vec<_>, EbpfError<BpfError>>>()?;
let translate = |account_info: &SolAccountInfo, let translate = |account_info: &SolAccountInfo, invoke_context: &mut dyn InvokeContext| {
invoke_context: &Ref<&mut dyn InvokeContext>| {
// Translate the account from user space // Translate the account from user space
let lamports = translate_type_mut::<u64>( let lamports = translate_type_mut::<u64>(
@ -1967,31 +1944,23 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
self.loader_id, self.loader_id,
)?; )?;
Ok(( Ok(AccountReferences {
Rc::new(RefCell::new(AccountSharedData::from(Account { lamports,
lamports: *lamports, owner,
data: data.to_vec(), data,
executable: account_info.executable, vm_data_addr,
owner: *owner, ref_to_len_in_vm,
rent_epoch: account_info.rent_epoch, serialized_len_ptr,
}))), executable: account_info.executable,
Some(AccountReferences { rent_epoch: account_info.rent_epoch,
lamports, })
owner,
data,
vm_data_addr,
ref_to_len_in_vm,
serialized_len_ptr,
}),
))
}; };
get_translated_accounts( get_translated_accounts(
account_keys, message,
program_account_index,
&account_info_keys, &account_info_keys,
account_infos, account_infos,
&invoke_context, invoke_context,
translate, translate,
) )
} }
@ -2072,56 +2041,55 @@ impl<'a> SyscallObject<BpfError> for SyscallInvokeSignedC<'a> {
} }
fn get_translated_accounts<'a, T, F>( fn get_translated_accounts<'a, T, F>(
account_keys: &[Pubkey], message: &Message,
program_account_index: usize,
account_info_keys: &[&Pubkey], account_info_keys: &[&Pubkey],
account_infos: &[T], account_infos: &[T],
invoke_context: &Ref<&mut dyn InvokeContext>, invoke_context: &mut dyn InvokeContext,
do_translate: F, do_translate: F,
) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>> ) -> Result<TranslatedAccounts<'a>, EbpfError<BpfError>>
where where
F: Fn(&T, &Ref<&mut dyn InvokeContext>) -> Result<TranslatedAccount<'a>, EbpfError<BpfError>>, F: Fn(&T, &mut dyn InvokeContext) -> Result<AccountReferences<'a>, EbpfError<BpfError>>,
{ {
let mut accounts = Vec::with_capacity(account_keys.len()); let demote_program_write_locks =
let mut refs = Vec::with_capacity(account_keys.len()); invoke_context.is_feature_active(&demote_program_write_locks::id());
for (i, ref account_key) in account_keys.iter().enumerate() { let mut accounts = Vec::with_capacity(message.account_keys.len());
let (_account_index, account) = let mut refs = Vec::with_capacity(message.account_keys.len());
invoke_context.get_account(account_key).ok_or_else(|| { for (i, account_key) in message.account_keys.iter().enumerate() {
ic_msg!( if let Some((_account_index, account)) = invoke_context.get_account(account_key) {
invoke_context, if i == message.instructions[0].program_id_index as usize
"Instruction references an unknown account {}", || account.borrow().executable()
account_key {
); // Use the known account
SyscallError::InstructionError(InstructionError::MissingAccount) accounts.push((*account_key, account));
})?; refs.push(None);
continue;
if i == program_account_index || account.borrow().executable() { } else if let Some(account_ref_index) =
// Use the known account account_info_keys.iter().position(|key| *key == account_key)
accounts.push((**account_key, account)); {
refs.push(None); let account_ref = do_translate(&account_infos[account_ref_index], invoke_context)?;
} else if let Some(account_info) = {
account_info_keys let mut account = account.borrow_mut();
.iter() account.copy_into_owner_from_slice(account_ref.owner.as_ref());
.zip(account_infos) account.set_data_from_slice(account_ref.data);
.find_map(|(key, account_info)| { account.set_lamports(*account_ref.lamports);
if key == account_key { account.set_executable(account_ref.executable);
Some(account_info) account.set_rent_epoch(account_ref.rent_epoch);
} else { }
None accounts.push((*account_key, account));
} refs.push(if message.is_writable(i, demote_program_write_locks) {
}) Some(account_ref)
{ } else {
let (account, account_ref) = do_translate(account_info, invoke_context)?; None
accounts.push((**account_key, account)); });
refs.push(account_ref); continue;
} else { }
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
} }
ic_msg!(
invoke_context,
"Instruction references an unknown account {}",
account_key
);
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
} }
Ok((accounts, refs)) Ok((accounts, refs))
@ -2130,7 +2098,7 @@ where
fn check_instruction_size( fn check_instruction_size(
num_accounts: usize, num_accounts: usize,
data_len: usize, data_len: usize,
invoke_context: &Ref<&mut dyn InvokeContext>, invoke_context: &mut dyn InvokeContext,
) -> Result<(), EbpfError<BpfError>> { ) -> Result<(), EbpfError<BpfError>> {
let size = num_accounts let size = num_accounts
.saturating_mul(size_of::<AccountMeta>()) .saturating_mul(size_of::<AccountMeta>())
@ -2144,7 +2112,7 @@ fn check_instruction_size(
fn check_account_infos( fn check_account_infos(
len: usize, len: usize,
invoke_context: &Ref<&mut dyn InvokeContext>, invoke_context: &mut dyn InvokeContext,
) -> Result<(), EbpfError<BpfError>> { ) -> Result<(), EbpfError<BpfError>> {
if len * size_of::<Pubkey>() > invoke_context.get_compute_budget().max_cpi_instruction_size { if len * size_of::<Pubkey>() > invoke_context.get_compute_budget().max_cpi_instruction_size {
// Cap the number of account_infos a caller can pass to approximate // Cap the number of account_infos a caller can pass to approximate
@ -2173,44 +2141,6 @@ fn check_authorized_program(
Ok(()) Ok(())
} }
#[allow(clippy::type_complexity)]
fn get_upgradeable_executable(
callee_program_id: &Pubkey,
program_account: &Rc<RefCell<AccountSharedData>>,
invoke_context: &Ref<&mut dyn InvokeContext>,
) -> Result<Option<usize>, EbpfError<BpfError>> {
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
match program_account.borrow().state() {
Ok(UpgradeableLoaderState::Program {
programdata_address,
}) => {
if let Some((programdata_account_index, _programdata_account)) =
invoke_context.get_account(&programdata_address)
{
Ok(Some(programdata_account_index))
} else {
ic_msg!(
invoke_context,
"Unknown upgradeable programdata account {}",
programdata_address,
);
Err(SyscallError::InstructionError(InstructionError::MissingAccount).into())
}
}
_ => {
ic_msg!(
invoke_context,
"Invalid upgradeable program account {}",
callee_program_id,
);
Err(SyscallError::InstructionError(InstructionError::InvalidAccountData).into())
}
}
} else {
Ok(None)
}
}
/// Call process instruction, common to both Rust and C /// Call process instruction, common to both Rust and C
fn call<'a>( fn call<'a>(
syscall: &mut dyn SyscallInvokeSigned<'a>, syscall: &mut dyn SyscallInvokeSigned<'a>,
@ -2221,174 +2151,92 @@ fn call<'a>(
signers_seeds_len: u64, signers_seeds_len: u64,
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
) -> Result<u64, EbpfError<BpfError>> { ) -> Result<u64, EbpfError<BpfError>> {
let ( let mut invoke_context = syscall.get_context_mut()?;
message, invoke_context
program_indices, .get_compute_meter()
accounts, .consume(invoke_context.get_compute_budget().invoke_units)?;
account_refs,
caller_write_privileges,
demote_program_write_locks,
) = {
let invoke_context = syscall.get_context()?;
invoke_context // Translate and verify caller's data
.get_compute_meter() let instruction =
.consume(invoke_context.get_compute_budget().invoke_units)?; syscall.translate_instruction(instruction_addr, memory_mapping, *invoke_context)?;
let caller_program_id = invoke_context
let caller_program_id = invoke_context .get_caller()
.get_caller() .map_err(SyscallError::InstructionError)?;
let signers = syscall.translate_signers(
caller_program_id,
signers_seeds_addr,
signers_seeds_len,
memory_mapping,
)?;
let (message, caller_write_privileges, program_indices) =
InstructionProcessor::create_message(&instruction, &signers, &invoke_context)
.map_err(SyscallError::InstructionError)?; .map_err(SyscallError::InstructionError)?;
check_authorized_program(
&instruction.program_id,
&instruction.data,
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
)?;
let (accounts, account_refs) = syscall.translate_accounts(
&message,
account_infos_addr,
account_infos_len,
memory_mapping,
*invoke_context,
)?;
// Translate and verify caller's data // Record the instruction
invoke_context.record_instruction(&instruction);
let instruction = syscall.translate_instruction(instruction_addr, memory_mapping)?;
let signers = syscall.translate_signers(
caller_program_id,
signers_seeds_addr,
signers_seeds_len,
memory_mapping,
)?;
let keyed_account_refs = invoke_context
.get_keyed_accounts()
.map_err(SyscallError::InstructionError)?
.iter()
.collect::<Vec<&KeyedAccount>>();
let (message, callee_program_id, callee_program_id_index) =
InstructionProcessor::create_message(
&instruction,
&keyed_account_refs,
&signers,
&invoke_context,
)
.map_err(SyscallError::InstructionError)?;
let caller_write_privileges = message
.account_keys
.iter()
.map(|key| {
if let Some(keyed_account) = keyed_account_refs
.iter()
.find(|keyed_account| key == keyed_account.unsigned_key())
{
keyed_account.is_writable()
} else {
false
}
})
.collect::<Vec<bool>>();
check_authorized_program(
&callee_program_id,
&instruction.data,
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
)?;
let (accounts, account_refs) = syscall.translate_accounts(
&message.account_keys,
callee_program_id_index,
account_infos_addr,
account_infos_len,
memory_mapping,
)?;
// Construct executables
let program_account = accounts
.get(callee_program_id_index)
.ok_or_else(|| {
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
SyscallError::InstructionError(InstructionError::MissingAccount)
})?
.1
.clone();
let (program_account_index, _program_account) =
invoke_context.get_account(&callee_program_id).ok_or(
SyscallError::InstructionError(InstructionError::MissingAccount),
)?;
let mut program_indices = vec![program_account_index];
if let Some(programdata_account_index) =
get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?
{
program_indices.push(programdata_account_index);
}
// Record the instruction
invoke_context.record_instruction(&instruction);
(
message,
program_indices,
accounts,
account_refs,
caller_write_privileges,
invoke_context.is_feature_active(&demote_program_write_locks::id()),
)
};
// Process instruction // Process instruction
InstructionProcessor::process_cross_program_instruction(
#[allow(clippy::deref_addrof)]
match InstructionProcessor::process_cross_program_instruction(
&message, &message,
&program_indices, &program_indices,
&accounts, &accounts,
&caller_write_privileges, &caller_write_privileges,
*(&mut *(syscall.get_context_mut()?)), *invoke_context,
) { )
Ok(()) => (), .map_err(SyscallError::InstructionError)?;
Err(err) => {
return Err(SyscallError::InstructionError(err).into());
}
}
// Copy results back to caller // Copy results back to caller
{ for ((_key, account), account_ref) in accounts.iter().zip(account_refs) {
let invoke_context = syscall.get_context()?; let account = account.borrow();
for (i, ((_key, account), account_ref)) in accounts.iter().zip(account_refs).enumerate() { if let Some(mut account_ref) = account_ref {
let account = account.borrow(); *account_ref.lamports = account.lamports();
if let Some(mut account_ref) = account_ref { *account_ref.owner = *account.owner();
if message.is_writable(i, demote_program_write_locks) && !account.executable() { if account_ref.data.len() != account.data().len() {
*account_ref.lamports = account.lamports(); if !account_ref.data.is_empty() {
*account_ref.owner = *account.owner(); // Only support for `CreateAccount` at this time.
if account_ref.data.len() != account.data().len() { // Need a way to limit total realloc size across multiple CPI calls
if !account_ref.data.is_empty() { ic_msg!(
// Only support for `CreateAccount` at this time. invoke_context,
// Need a way to limit total realloc size across multiple CPI calls "Inner instructions do not support realloc, only SystemProgram::CreateAccount",
ic_msg!( );
invoke_context, return Err(
"Inner instructions do not support realloc, only SystemProgram::CreateAccount", SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
); );
return Err(SyscallError::InstructionError(
InstructionError::InvalidRealloc,
)
.into());
}
if account.data().len()
> account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE
{
ic_msg!(
invoke_context,
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
MAX_PERMITTED_DATA_INCREASE
);
return Err(SyscallError::InstructionError(
InstructionError::InvalidRealloc,
)
.into());
}
account_ref.data = translate_slice_mut::<u8>(
memory_mapping,
account_ref.vm_data_addr,
account.data().len() as u64,
&bpf_loader_deprecated::id(), // Don't care since it is byte aligned
)?;
*account_ref.ref_to_len_in_vm = account.data().len() as u64;
*account_ref.serialized_len_ptr = account.data().len() as u64;
}
account_ref
.data
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
} }
if account.data().len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
ic_msg!(
invoke_context,
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
MAX_PERMITTED_DATA_INCREASE
);
return Err(
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
);
}
account_ref.data = translate_slice_mut::<u8>(
memory_mapping,
account_ref.vm_data_addr,
account.data().len() as u64,
&bpf_loader_deprecated::id(), // Don't care since it is byte aligned
)?;
*account_ref.ref_to_len_in_vm = account.data().len() as u64;
*account_ref.serialized_len_ptr = account.data().len() as u64;
} }
account_ref
.data
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
} }
} }

View File

@ -170,7 +170,8 @@ native machine code before execting it in the virtual machine.",
} }
let lid = bpf_loader::id(); let lid = bpf_loader::id();
let pid = Pubkey::new(&[0u8; 32]); let pid = Pubkey::new(&[0u8; 32]);
let mut bytes = serialize_parameters(&lid, &pid, &accounts, &input.insndata).unwrap(); let (mut bytes, _account_lenghts) =
serialize_parameters(&lid, &pid, &accounts, &input.insndata).unwrap();
Vec::from(bytes.as_slice_mut()) Vec::from(bytes.as_slice_mut())
} }
}; };