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:
parent
efd024510a
commit
36f46e1c31
|
@ -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,119 +441,17 @@ 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()? {
|
|
||||||
ic_msg!(
|
|
||||||
invoke_context,
|
|
||||||
"Account {} is not executable",
|
|
||||||
keyed_account.unsigned_key()
|
|
||||||
);
|
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
ic_msg!(invoke_context, "Unknown program {}", program_id);
|
|
||||||
return Err(InstructionError::MissingAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = Message::new(&[instruction.clone()], None);
|
// Find and validate executables / program accounts
|
||||||
let program_id_index = message.instructions[0].program_id_index as usize;
|
let callee_program_id = instruction.program_id;
|
||||||
|
let (program_account_index, program_account) = callee_keyed_accounts
|
||||||
Ok((message, program_id, program_id_index))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entrypoint for a cross-program invocation from a native program
|
|
||||||
pub fn native_invoke(
|
|
||||||
invoke_context: &mut dyn InvokeContext,
|
|
||||||
instruction: Instruction,
|
|
||||||
keyed_account_indices: &[usize],
|
|
||||||
signers: &[Pubkey],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
let invoke_context = RefCell::new(invoke_context);
|
|
||||||
|
|
||||||
let (
|
|
||||||
message,
|
|
||||||
program_indices,
|
|
||||||
accounts,
|
|
||||||
keyed_account_indices_reordered,
|
|
||||||
caller_write_privileges,
|
|
||||||
) = {
|
|
||||||
let invoke_context = invoke_context.borrow();
|
|
||||||
|
|
||||||
let caller_keyed_accounts = invoke_context.get_keyed_accounts()?;
|
|
||||||
let callee_keyed_accounts = keyed_account_indices
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|index| keyed_account_at_index(caller_keyed_accounts, *index))
|
.find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
|
||||||
.collect::<Result<Vec<&KeyedAccount>, InstructionError>>()?;
|
.and_then(|_keyed_account| invoke_context.get_account(&callee_program_id))
|
||||||
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(|| {
|
.ok_or_else(|| {
|
||||||
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
|
ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
|
||||||
InstructionError::MissingAccount
|
InstructionError::MissingAccount
|
||||||
|
@ -546,7 +464,7 @@ impl InstructionProcessor {
|
||||||
);
|
);
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
}
|
}
|
||||||
let mut program_indices = vec![];
|
let mut program_indices = vec![program_account_index];
|
||||||
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
|
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
|
||||||
if let UpgradeableLoaderState::Program {
|
if let UpgradeableLoaderState::Program {
|
||||||
programdata_address,
|
programdata_address,
|
||||||
|
@ -567,51 +485,67 @@ impl InstructionProcessor {
|
||||||
} else {
|
} else {
|
||||||
ic_msg!(
|
ic_msg!(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
"Upgradeable program account state not valid {}",
|
"Invalid upgradeable program account {}",
|
||||||
callee_program_id,
|
callee_program_id,
|
||||||
);
|
);
|
||||||
return Err(InstructionError::MissingAccount);
|
return Err(InstructionError::MissingAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
program_indices.insert(0, program_account_index);
|
|
||||||
(
|
|
||||||
message,
|
|
||||||
program_indices,
|
|
||||||
accounts,
|
|
||||||
keyed_account_indices_reordered,
|
|
||||||
caller_write_privileges,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(clippy::deref_addrof)]
|
Ok((message, caller_write_privileges, program_indices))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entrypoint for a cross-program invocation from a native program
|
||||||
|
pub fn native_invoke(
|
||||||
|
invoke_context: &mut dyn InvokeContext,
|
||||||
|
instruction: Instruction,
|
||||||
|
keyed_account_indices_obsolete: &[usize],
|
||||||
|
signers: &[Pubkey],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let invoke_context = RefCell::new(invoke_context);
|
||||||
|
let mut invoke_context = invoke_context.borrow_mut();
|
||||||
|
|
||||||
|
// Translate and verify caller's data
|
||||||
|
let (message, mut caller_write_privileges, program_indices) =
|
||||||
|
Self::create_message(&instruction, signers, &invoke_context)?;
|
||||||
|
if !invoke_context.is_feature_active(&fix_write_privs::id()) {
|
||||||
|
let caller_keyed_accounts = invoke_context.get_keyed_accounts()?;
|
||||||
|
caller_write_privileges = Vec::with_capacity(1 + keyed_account_indices_obsolete.len());
|
||||||
|
caller_write_privileges.push(false);
|
||||||
|
for index in keyed_account_indices_obsolete.iter() {
|
||||||
|
caller_write_privileges.push(caller_keyed_accounts[*index].is_writable());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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<_>>();
|
||||||
|
|
||||||
|
// 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();
|
|
||||||
let demote_program_write_locks =
|
|
||||||
invoke_context.is_feature_active(&demote_program_write_locks::id());
|
|
||||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
|
||||||
for (src_keyed_account_index, ((_key, account), dst_keyed_account_index)) in accounts
|
|
||||||
.iter()
|
|
||||||
.zip(keyed_account_indices_reordered)
|
|
||||||
.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.
|
// Only support for `CreateAccount` at this time.
|
||||||
// Need a way to limit total realloc size across multiple CPI calls
|
// Need a way to limit total realloc size across multiple CPI calls
|
||||||
ic_msg!(
|
ic_msg!(
|
||||||
|
@ -620,17 +554,6 @@ impl InstructionProcessor {
|
||||||
);
|
);
|
||||||
return Err(InstructionError::InvalidRealloc);
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -645,7 +568,12 @@ 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 instruction = message
|
||||||
|
.instructions
|
||||||
|
.get(0)
|
||||||
|
.ok_or(InstructionError::GenericError)?;
|
||||||
|
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = instruction.program_id(&message.account_keys);
|
||||||
|
|
||||||
// Verify the calling program hasn't misbehaved
|
// Verify the calling program hasn't misbehaved
|
||||||
|
@ -680,10 +608,6 @@ impl InstructionProcessor {
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
result
|
result
|
||||||
} else {
|
|
||||||
// This function is always called with a valid instruction, if that changes return an error
|
|
||||||
Err(InstructionError::GenericError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the results of a cross-program instruction
|
/// Verify the results of a cross-program instruction
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
¶meter_accounts,
|
¶meter_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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,7 +462,12 @@ mod tests {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
deserialize_parameters(&bpf_loader::id(), &de_keyed_accounts, serialized.as_slice())
|
deserialize_parameters(
|
||||||
|
&bpf_loader::id(),
|
||||||
|
&de_keyed_accounts,
|
||||||
|
serialized.as_slice(),
|
||||||
|
&account_lengths,
|
||||||
|
)
|
||||||
.unwrap();
|
.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
|
||||||
|
|
|
@ -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,
|
|
||||||
data: data.to_vec(),
|
|
||||||
executable: account_info.executable,
|
|
||||||
owner: *owner,
|
|
||||||
rent_epoch: account_info.rent_epoch,
|
|
||||||
}))),
|
|
||||||
Some(AccountReferences {
|
|
||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
vm_data_addr,
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
}),
|
executable: account_info.executable,
|
||||||
))
|
rent_epoch: account_info.rent_epoch,
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
|
||||||
data: data.to_vec(),
|
|
||||||
executable: account_info.executable,
|
|
||||||
owner: *owner,
|
|
||||||
rent_epoch: account_info.rent_epoch,
|
|
||||||
}))),
|
|
||||||
Some(AccountReferences {
|
|
||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
vm_data_addr,
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
}),
|
executable: account_info.executable,
|
||||||
))
|
rent_epoch: account_info.rent_epoch,
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
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,49 +2041,49 @@ 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
|
{
|
||||||
);
|
|
||||||
SyscallError::InstructionError(InstructionError::MissingAccount)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if i == program_account_index || account.borrow().executable() {
|
|
||||||
// Use the known account
|
// Use the known account
|
||||||
accounts.push((**account_key, account));
|
accounts.push((*account_key, account));
|
||||||
refs.push(None);
|
refs.push(None);
|
||||||
} else if let Some(account_info) =
|
continue;
|
||||||
account_info_keys
|
} else if let Some(account_ref_index) =
|
||||||
.iter()
|
account_info_keys.iter().position(|key| *key == account_key)
|
||||||
.zip(account_infos)
|
{
|
||||||
.find_map(|(key, account_info)| {
|
let account_ref = do_translate(&account_infos[account_ref_index], invoke_context)?;
|
||||||
if key == account_key {
|
{
|
||||||
Some(account_info)
|
let mut account = account.borrow_mut();
|
||||||
|
account.copy_into_owner_from_slice(account_ref.owner.as_ref());
|
||||||
|
account.set_data_from_slice(account_ref.data);
|
||||||
|
account.set_lamports(*account_ref.lamports);
|
||||||
|
account.set_executable(account_ref.executable);
|
||||||
|
account.set_rent_epoch(account_ref.rent_epoch);
|
||||||
|
}
|
||||||
|
accounts.push((*account_key, account));
|
||||||
|
refs.push(if message.is_writable(i, demote_program_write_locks) {
|
||||||
|
Some(account_ref)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
{
|
|
||||||
let (account, account_ref) = do_translate(account_info, invoke_context)?;
|
|
||||||
accounts.push((**account_key, account));
|
|
||||||
refs.push(account_ref);
|
|
||||||
} else {
|
|
||||||
ic_msg!(
|
ic_msg!(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
"Instruction references an unknown account {}",
|
"Instruction references an unknown account {}",
|
||||||
|
@ -2122,7 +2091,6 @@ where
|
||||||
);
|
);
|
||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
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,132 +2151,56 @@ 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,
|
|
||||||
program_indices,
|
|
||||||
accounts,
|
|
||||||
account_refs,
|
|
||||||
caller_write_privileges,
|
|
||||||
demote_program_write_locks,
|
|
||||||
) = {
|
|
||||||
let invoke_context = syscall.get_context()?;
|
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
.consume(invoke_context.get_compute_budget().invoke_units)?;
|
.consume(invoke_context.get_compute_budget().invoke_units)?;
|
||||||
|
|
||||||
|
// Translate and verify caller's data
|
||||||
|
let instruction =
|
||||||
|
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)?;
|
.map_err(SyscallError::InstructionError)?;
|
||||||
|
|
||||||
// Translate and verify caller's data
|
|
||||||
|
|
||||||
let instruction = syscall.translate_instruction(instruction_addr, memory_mapping)?;
|
|
||||||
let signers = syscall.translate_signers(
|
let signers = syscall.translate_signers(
|
||||||
caller_program_id,
|
caller_program_id,
|
||||||
signers_seeds_addr,
|
signers_seeds_addr,
|
||||||
signers_seeds_len,
|
signers_seeds_len,
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
)?;
|
)?;
|
||||||
let keyed_account_refs = invoke_context
|
let (message, caller_write_privileges, program_indices) =
|
||||||
.get_keyed_accounts()
|
InstructionProcessor::create_message(&instruction, &signers, &invoke_context)
|
||||||
.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)?;
|
.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(
|
check_authorized_program(
|
||||||
&callee_program_id,
|
&instruction.program_id,
|
||||||
&instruction.data,
|
&instruction.data,
|
||||||
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
|
invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()),
|
||||||
)?;
|
)?;
|
||||||
let (accounts, account_refs) = syscall.translate_accounts(
|
let (accounts, account_refs) = syscall.translate_accounts(
|
||||||
&message.account_keys,
|
&message,
|
||||||
callee_program_id_index,
|
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
account_infos_len,
|
account_infos_len,
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
|
*invoke_context,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 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
|
// Record the instruction
|
||||||
|
|
||||||
invoke_context.record_instruction(&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()?;
|
|
||||||
for (i, ((_key, account), account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
|
||||||
let account = account.borrow();
|
let account = account.borrow();
|
||||||
if let Some(mut account_ref) = account_ref {
|
if let Some(mut account_ref) = account_ref {
|
||||||
if message.is_writable(i, demote_program_write_locks) && !account.executable() {
|
|
||||||
*account_ref.lamports = account.lamports();
|
*account_ref.lamports = account.lamports();
|
||||||
*account_ref.owner = *account.owner();
|
*account_ref.owner = *account.owner();
|
||||||
if account_ref.data.len() != account.data().len() {
|
if account_ref.data.len() != account.data().len() {
|
||||||
|
@ -2357,23 +2211,19 @@ fn call<'a>(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
|
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
|
||||||
);
|
);
|
||||||
return Err(SyscallError::InstructionError(
|
return Err(
|
||||||
InstructionError::InvalidRealloc,
|
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
||||||
)
|
);
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
if account.data().len()
|
if account.data().len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
|
||||||
> account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE
|
|
||||||
{
|
|
||||||
ic_msg!(
|
ic_msg!(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
|
"SystemProgram::CreateAccount data size limited to {} in inner instructions",
|
||||||
MAX_PERMITTED_DATA_INCREASE
|
MAX_PERMITTED_DATA_INCREASE
|
||||||
);
|
);
|
||||||
return Err(SyscallError::InstructionError(
|
return Err(
|
||||||
InstructionError::InvalidRealloc,
|
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
||||||
)
|
);
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
account_ref.data = translate_slice_mut::<u8>(
|
account_ref.data = translate_slice_mut::<u8>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
|
@ -2389,8 +2239,6 @@ fn call<'a>(
|
||||||
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
|
.copy_from_slice(&account.data()[0..account_ref.data.len()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(SUCCESS)
|
Ok(SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue