Richer runtime failure logging (#14875)
This commit is contained in:
parent
a53b8558cd
commit
0b1015f7d3
|
@ -916,13 +916,13 @@ fn test_program_bpf_invoke_sanity() {
|
||||||
|
|
||||||
do_invoke_failure_test_local(
|
do_invoke_failure_test_local(
|
||||||
TEST_INSTRUCTION_DATA_TOO_LARGE,
|
TEST_INSTRUCTION_DATA_TOO_LARGE,
|
||||||
TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded),
|
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
do_invoke_failure_test_local(
|
do_invoke_failure_test_local(
|
||||||
TEST_INSTRUCTION_META_TOO_LARGE,
|
TEST_INSTRUCTION_META_TOO_LARGE,
|
||||||
TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded),
|
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -779,17 +779,15 @@ impl Executor for BPFExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
ic_logger_msg!(logger, "Program {} BPF VM error: {}", program_id, error);
|
|
||||||
let error = match error {
|
let error = match error {
|
||||||
EbpfError::UserError(BPFError::SyscallError(
|
EbpfError::UserError(BPFError::SyscallError(
|
||||||
SyscallError::InstructionError(error),
|
SyscallError::InstructionError(error),
|
||||||
)) => error,
|
)) => error,
|
||||||
err => {
|
err => {
|
||||||
ic_logger_msg!(logger, "Program failed to complete: {:?}", err);
|
ic_logger_msg!(logger, "Program failed to complete: {}", err);
|
||||||
InstructionError::ProgramFailedToComplete
|
InstructionError::ProgramFailedToComplete
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
stable_log::program_failure(&logger, program_id, &error);
|
stable_log::program_failure(&logger, program_id, &error);
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ use solana_sdk::{
|
||||||
use_loaded_program_accounts,
|
use_loaded_program_accounts,
|
||||||
},
|
},
|
||||||
hash::{Hasher, HASH_BYTES},
|
hash::{Hasher, HASH_BYTES},
|
||||||
|
ic_msg,
|
||||||
instruction::{AccountMeta, Instruction, InstructionError},
|
instruction::{AccountMeta, Instruction, InstructionError},
|
||||||
keyed_account::KeyedAccount,
|
keyed_account::KeyedAccount,
|
||||||
native_loader,
|
native_loader,
|
||||||
|
@ -59,14 +60,18 @@ pub enum SyscallError {
|
||||||
MalformedSignerSeed(Utf8Error, Vec<u8>),
|
MalformedSignerSeed(Utf8Error, Vec<u8>),
|
||||||
#[error("Could not create program address with signer seeds: {0}")]
|
#[error("Could not create program address with signer seeds: {0}")]
|
||||||
BadSeeds(PubkeyError),
|
BadSeeds(PubkeyError),
|
||||||
#[error("Program id is not supported by cross-program invocations")]
|
#[error("Program {0} supported by inner instructions")]
|
||||||
ProgramNotSupported,
|
ProgramNotSupported(Pubkey),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InstructionError(InstructionError),
|
InstructionError(InstructionError),
|
||||||
#[error("Unaligned pointer")]
|
#[error("Unaligned pointer")]
|
||||||
UnalignedPointer,
|
UnalignedPointer,
|
||||||
#[error("Too many signers")]
|
#[error("Too many signers")]
|
||||||
TooManySigners,
|
TooManySigners,
|
||||||
|
#[error("Instruction passed to inner instruction is too large")]
|
||||||
|
InstructionTooLarge,
|
||||||
|
#[error("Too many accounts passed to inner instruction")]
|
||||||
|
TooManyAccounts,
|
||||||
}
|
}
|
||||||
impl From<SyscallError> for EbpfError<BPFError> {
|
impl From<SyscallError> for EbpfError<BPFError> {
|
||||||
fn from(error: SyscallError) -> Self {
|
fn from(error: SyscallError) -> Self {
|
||||||
|
@ -898,10 +903,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
||||||
check_instruction_size(
|
check_instruction_size(
|
||||||
ix.accounts.len(),
|
ix.accounts.len(),
|
||||||
ix.data.len(),
|
ix.data.len(),
|
||||||
self.invoke_context
|
&self.invoke_context.borrow(),
|
||||||
.borrow()
|
|
||||||
.get_bpf_compute_budget()
|
|
||||||
.max_cpi_instruction_size,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let accounts = translate_slice::<AccountMeta>(
|
let accounts = translate_slice::<AccountMeta>(
|
||||||
|
@ -1186,10 +1188,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
||||||
check_instruction_size(
|
check_instruction_size(
|
||||||
ix_c.accounts_len,
|
ix_c.accounts_len,
|
||||||
ix_c.data_len,
|
ix_c.data_len,
|
||||||
self.invoke_context
|
&self.invoke_context.borrow(),
|
||||||
.borrow()
|
|
||||||
.get_bpf_compute_budget()
|
|
||||||
.max_cpi_instruction_size,
|
|
||||||
)?;
|
)?;
|
||||||
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)?;
|
||||||
|
@ -1408,12 +1407,14 @@ where
|
||||||
let mut accounts = Vec::with_capacity(account_keys.len());
|
let mut accounts = Vec::with_capacity(account_keys.len());
|
||||||
let mut refs = Vec::with_capacity(account_keys.len());
|
let mut refs = Vec::with_capacity(account_keys.len());
|
||||||
for (i, ref account_key) in account_keys.iter().enumerate() {
|
for (i, ref account_key) in account_keys.iter().enumerate() {
|
||||||
let account =
|
let account = invoke_context.get_account(&account_key).ok_or_else(|| {
|
||||||
invoke_context
|
ic_msg!(
|
||||||
.get_account(&account_key)
|
invoke_context,
|
||||||
.ok_or(SyscallError::InstructionError(
|
"Instruction references an unknown account {:?}",
|
||||||
InstructionError::MissingAccount,
|
account_key
|
||||||
))?;
|
);
|
||||||
|
SyscallError::InstructionError(InstructionError::MissingAccount)
|
||||||
|
})?;
|
||||||
|
|
||||||
if (invoke_context.is_feature_active(&use_loaded_program_accounts::id())
|
if (invoke_context.is_feature_active(&use_loaded_program_accounts::id())
|
||||||
&& i == program_account_index)
|
&& i == program_account_index)
|
||||||
|
@ -1439,6 +1440,11 @@ where
|
||||||
accounts.push(account);
|
accounts.push(account);
|
||||||
refs.push(account_ref);
|
refs.push(account_ref);
|
||||||
} else {
|
} else {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Instruction references an unknown account {:?}",
|
||||||
|
account_key
|
||||||
|
);
|
||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1449,12 +1455,14 @@ where
|
||||||
fn check_instruction_size(
|
fn check_instruction_size(
|
||||||
num_accounts: usize,
|
num_accounts: usize,
|
||||||
data_len: usize,
|
data_len: usize,
|
||||||
max_size: usize,
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
) -> Result<(), EbpfError<BPFError>> {
|
) -> Result<(), EbpfError<BPFError>> {
|
||||||
if max_size < num_accounts * size_of::<AccountMeta>() + data_len {
|
if invoke_context
|
||||||
return Err(
|
.get_bpf_compute_budget()
|
||||||
SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded).into(),
|
.max_cpi_instruction_size
|
||||||
);
|
< num_accounts * size_of::<AccountMeta>() + data_len
|
||||||
|
{
|
||||||
|
return Err(SyscallError::InstructionTooLarge.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1471,9 +1479,7 @@ fn check_account_infos(
|
||||||
{
|
{
|
||||||
// Cap the number of account_infos a caller can pass to approximate
|
// Cap the number of account_infos a caller can pass to approximate
|
||||||
// maximum that accounts that could be passed in an instruction
|
// maximum that accounts that could be passed in an instruction
|
||||||
return Err(
|
return Err(SyscallError::TooManyAccounts.into());
|
||||||
SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded).into(),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1488,30 +1494,40 @@ fn check_authorized_program(
|
||||||
|| (bpf_loader_upgradeable::check_id(program_id)
|
|| (bpf_loader_upgradeable::check_id(program_id)
|
||||||
&& !bpf_loader_upgradeable::is_upgrade_instruction(instruction_data))
|
&& !bpf_loader_upgradeable::is_upgrade_instruction(instruction_data))
|
||||||
{
|
{
|
||||||
return Err(SyscallError::InstructionError(InstructionError::UnsupportedProgramId).into());
|
return Err(SyscallError::ProgramNotSupported(*program_id).into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_upgradeable_executable(
|
fn get_upgradeable_executable(
|
||||||
|
callee_program_id: &Pubkey,
|
||||||
program_account: &RefCell<Account>,
|
program_account: &RefCell<Account>,
|
||||||
invoke_context: &Ref<&mut dyn InvokeContext>,
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
) -> Result<Option<(Pubkey, RefCell<Account>)>, EbpfError<BPFError>> {
|
) -> Result<Option<(Pubkey, RefCell<Account>)>, EbpfError<BPFError>> {
|
||||||
if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
||||||
if let UpgradeableLoaderState::Program {
|
match program_account.borrow().state() {
|
||||||
programdata_address,
|
Ok(UpgradeableLoaderState::Program {
|
||||||
} = program_account
|
programdata_address,
|
||||||
.borrow()
|
}) => {
|
||||||
.state()
|
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
||||||
.map_err(SyscallError::InstructionError)?
|
Ok(Some((programdata_address, account)))
|
||||||
{
|
} else {
|
||||||
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
ic_msg!(
|
||||||
Ok(Some((programdata_address, account)))
|
invoke_context,
|
||||||
} else {
|
"Unknown upgradeable programdata account {:?}",
|
||||||
Err(SyscallError::InstructionError(InstructionError::MissingAccount).into())
|
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 {
|
|
||||||
Err(SyscallError::InstructionError(InstructionError::MissingAccount).into())
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -1560,8 +1576,13 @@ fn call<'a>(
|
||||||
.iter()
|
.iter()
|
||||||
.collect::<Vec<&KeyedAccount>>();
|
.collect::<Vec<&KeyedAccount>>();
|
||||||
let (message, callee_program_id, callee_program_id_index) =
|
let (message, callee_program_id, callee_program_id_index) =
|
||||||
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
|
MessageProcessor::create_message(
|
||||||
.map_err(SyscallError::InstructionError)?;
|
&instruction,
|
||||||
|
&keyed_account_refs,
|
||||||
|
&signers,
|
||||||
|
&invoke_context,
|
||||||
|
)
|
||||||
|
.map_err(SyscallError::InstructionError)?;
|
||||||
let caller_privileges = message
|
let caller_privileges = message
|
||||||
.account_keys
|
.account_keys
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1589,14 +1610,13 @@ fn call<'a>(
|
||||||
|
|
||||||
// Construct executables
|
// Construct executables
|
||||||
|
|
||||||
let program_account =
|
let program_account = (**accounts.get(callee_program_id_index).ok_or_else(|| {
|
||||||
(**accounts
|
ic_msg!(invoke_context, "Unknown program {:?}", callee_program_id,);
|
||||||
.get(callee_program_id_index)
|
SyscallError::InstructionError(InstructionError::MissingAccount)
|
||||||
.ok_or(SyscallError::InstructionError(
|
})?)
|
||||||
InstructionError::MissingAccount,
|
.clone();
|
||||||
))?)
|
let programdata_executable =
|
||||||
.clone();
|
get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?;
|
||||||
let programdata_executable = get_upgradeable_executable(&program_account, &invoke_context)?;
|
|
||||||
let mut executables = vec![(callee_program_id, program_account)];
|
let mut executables = vec![(callee_program_id, program_account)];
|
||||||
if let Some(executable) = programdata_executable {
|
if let Some(executable) = programdata_executable {
|
||||||
executables.push(executable);
|
executables.push(executable);
|
||||||
|
@ -1640,39 +1660,52 @@ fn call<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy results back to caller
|
// Copy results back to caller
|
||||||
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
{
|
||||||
let account = account.borrow();
|
let invoke_context = syscall.get_context()?;
|
||||||
if let Some(account_ref) = account_ref {
|
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
||||||
if message.is_writable(i) && !account.executable {
|
let account = account.borrow();
|
||||||
*account_ref.lamports = account.lamports;
|
if let Some(account_ref) = account_ref {
|
||||||
*account_ref.owner = account.owner;
|
if message.is_writable(i) && !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() {
|
||||||
return Err(SyscallError::InstructionError(
|
// Only support for `CreateAccount` at this time.
|
||||||
InstructionError::InvalidRealloc,
|
// Need a way to limit total realloc size across multiple CPI calls
|
||||||
)
|
ic_msg!(
|
||||||
.into());
|
invoke_context,
|
||||||
|
"Inner instructions do not support realloc, only SystemProgram::CreateAccount",
|
||||||
|
);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
let _ = translate(
|
||||||
|
memory_mapping,
|
||||||
|
AccessType::Store,
|
||||||
|
account_ref.vm_data_addr,
|
||||||
|
account.data.len() as u64,
|
||||||
|
)?;
|
||||||
|
*account_ref.ref_to_len_in_vm = account.data.len() as u64;
|
||||||
|
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
||||||
}
|
}
|
||||||
if account.data.len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
|
account_ref
|
||||||
return Err(SyscallError::InstructionError(
|
.data
|
||||||
InstructionError::InvalidRealloc,
|
.clone_from_slice(&account.data[0..account_ref.data.len()]);
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
let _ = translate(
|
|
||||||
memory_mapping,
|
|
||||||
AccessType::Store,
|
|
||||||
account_ref.vm_data_addr,
|
|
||||||
account.data.len() as u64,
|
|
||||||
)?;
|
|
||||||
*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
|
|
||||||
.clone_from_slice(&account.data[0..account_ref.data.len()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
feature_set::{instructions_sysvar_enabled, track_writable_deescalation, FeatureSet},
|
feature_set::{instructions_sysvar_enabled, track_writable_deescalation, FeatureSet},
|
||||||
|
ic_msg,
|
||||||
instruction::{CompiledInstruction, Instruction, InstructionError},
|
instruction::{CompiledInstruction, Instruction, InstructionError},
|
||||||
keyed_account::{create_keyed_readonly_accounts, KeyedAccount},
|
keyed_account::{create_keyed_readonly_accounts, KeyedAccount},
|
||||||
message::Message,
|
message::Message,
|
||||||
|
@ -22,7 +23,12 @@ use solana_sdk::{
|
||||||
system_program,
|
system_program,
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
use std::{
|
||||||
|
cell::{Ref, RefCell},
|
||||||
|
collections::HashMap,
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Executors {
|
pub struct Executors {
|
||||||
pub executors: HashMap<Pubkey, Arc<dyn Executor>>,
|
pub executors: HashMap<Pubkey, Arc<dyn Executor>>,
|
||||||
|
@ -525,6 +531,7 @@ impl MessageProcessor {
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
keyed_accounts: &[&KeyedAccount],
|
keyed_accounts: &[&KeyedAccount],
|
||||||
signers: &[Pubkey],
|
signers: &[Pubkey],
|
||||||
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
) -> Result<(Message, Pubkey, usize), InstructionError> {
|
) -> Result<(Message, Pubkey, usize), InstructionError> {
|
||||||
// Check for privilege escalation
|
// Check for privilege escalation
|
||||||
for account in instruction.accounts.iter() {
|
for account in instruction.accounts.iter() {
|
||||||
|
@ -537,9 +544,21 @@ impl MessageProcessor {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok_or(InstructionError::MissingAccount)?;
|
.ok_or_else(|| {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Instruction references an unknown account {:?}",
|
||||||
|
account.pubkey
|
||||||
|
);
|
||||||
|
InstructionError::MissingAccount
|
||||||
|
})?;
|
||||||
// Readonly account cannot become writable
|
// Readonly account cannot become writable
|
||||||
if account.is_writable && !keyed_account.is_writable() {
|
if account.is_writable && !keyed_account.is_writable() {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"{:?}'s writable priviledge escalated ",
|
||||||
|
account.pubkey
|
||||||
|
);
|
||||||
return Err(InstructionError::PrivilegeEscalation);
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,6 +567,11 @@ impl MessageProcessor {
|
||||||
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
||||||
|| signers.contains(&account.pubkey) // Signed by the program
|
|| signers.contains(&account.pubkey) // Signed by the program
|
||||||
) {
|
) {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"{:?}'s signer priviledge escalated ",
|
||||||
|
account.pubkey
|
||||||
|
);
|
||||||
return Err(InstructionError::PrivilegeEscalation);
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,7 +587,10 @@ impl MessageProcessor {
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => return Err(InstructionError::MissingAccount),
|
None => {
|
||||||
|
ic_msg!(invoke_context, "Unknown program {:?}", program_id);
|
||||||
|
return Err(InstructionError::MissingAccount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = Message::new(&[instruction.clone()], None);
|
let message = Message::new(&[instruction.clone()], None);
|
||||||
|
@ -579,87 +606,128 @@ impl MessageProcessor {
|
||||||
keyed_accounts: &[&KeyedAccount],
|
keyed_accounts: &[&KeyedAccount],
|
||||||
signers_seeds: &[&[&[u8]]],
|
signers_seeds: &[&[&[u8]]],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let caller_program_id = invoke_context.get_caller()?;
|
let invoke_context = RefCell::new(invoke_context);
|
||||||
|
|
||||||
// Translate and verify caller's data
|
let (message, executables, accounts, account_refs, caller_privileges) = {
|
||||||
|
let invoke_context = invoke_context.borrow();
|
||||||
|
|
||||||
let signers = signers_seeds
|
let caller_program_id = invoke_context.get_caller()?;
|
||||||
.iter()
|
|
||||||
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
|
// Translate and verify caller's data
|
||||||
.collect::<Result<Vec<_>, solana_sdk::pubkey::PubkeyError>>()?;
|
|
||||||
let mut caller_privileges = keyed_accounts
|
let signers = signers_seeds
|
||||||
.iter()
|
.iter()
|
||||||
.map(|keyed_account| keyed_account.is_writable())
|
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
|
||||||
.collect::<Vec<bool>>();
|
.collect::<Result<Vec<_>, solana_sdk::pubkey::PubkeyError>>()?;
|
||||||
caller_privileges.insert(0, false);
|
let mut caller_privileges = keyed_accounts
|
||||||
let (message, callee_program_id, _) =
|
.iter()
|
||||||
Self::create_message(&instruction, &keyed_accounts, &signers)?;
|
.map(|keyed_account| keyed_account.is_writable())
|
||||||
let mut accounts = vec![];
|
.collect::<Vec<bool>>();
|
||||||
let mut account_refs = vec![];
|
caller_privileges.insert(0, false);
|
||||||
'root: for account_key in message.account_keys.iter() {
|
let (message, callee_program_id, _) =
|
||||||
for keyed_account in keyed_accounts {
|
Self::create_message(&instruction, &keyed_accounts, &signers, &invoke_context)?;
|
||||||
if account_key == keyed_account.unsigned_key() {
|
let mut accounts = vec![];
|
||||||
accounts.push(Rc::new(keyed_account.account.clone()));
|
let mut account_refs = vec![];
|
||||||
account_refs.push(keyed_account);
|
'root: for account_key in message.account_keys.iter() {
|
||||||
continue 'root;
|
for keyed_account in keyed_accounts {
|
||||||
|
if account_key == keyed_account.unsigned_key() {
|
||||||
|
accounts.push(Rc::new(keyed_account.account.clone()));
|
||||||
|
account_refs.push(keyed_account);
|
||||||
|
continue 'root;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Instruction references an unknown account {:?}",
|
||||||
|
account_key
|
||||||
|
);
|
||||||
|
return Err(InstructionError::MissingAccount);
|
||||||
}
|
}
|
||||||
return Err(InstructionError::MissingAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process instruction
|
// Process instruction
|
||||||
|
|
||||||
invoke_context.record_instruction(&instruction);
|
invoke_context.record_instruction(&instruction);
|
||||||
|
|
||||||
let program_account = invoke_context
|
let program_account =
|
||||||
.get_account(&callee_program_id)
|
invoke_context
|
||||||
.ok_or(InstructionError::MissingAccount)?;
|
.get_account(&callee_program_id)
|
||||||
if !program_account.borrow().executable {
|
.ok_or_else(|| {
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
ic_msg!(invoke_context, "Unknown program {:?}", callee_program_id);
|
||||||
}
|
InstructionError::MissingAccount
|
||||||
let programdata_executable =
|
})?;
|
||||||
if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
if !program_account.borrow().executable {
|
||||||
if let UpgradeableLoaderState::Program {
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
programdata_address,
|
}
|
||||||
} = program_account.borrow().state()?
|
let programdata_executable =
|
||||||
{
|
if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
||||||
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
if let UpgradeableLoaderState::Program {
|
||||||
Some((programdata_address, account))
|
programdata_address,
|
||||||
|
} = program_account.borrow().state()?
|
||||||
|
{
|
||||||
|
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
||||||
|
Some((programdata_address, account))
|
||||||
|
} else {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Unknown upgradeable programdata account {:?}",
|
||||||
|
programdata_address,
|
||||||
|
);
|
||||||
|
return Err(InstructionError::MissingAccount);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ic_msg!(
|
||||||
|
invoke_context,
|
||||||
|
"Upgradeable program account state not valid {:?}",
|
||||||
|
callee_program_id,
|
||||||
|
);
|
||||||
return Err(InstructionError::MissingAccount);
|
return Err(InstructionError::MissingAccount);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(InstructionError::MissingAccount);
|
None
|
||||||
}
|
};
|
||||||
} else {
|
let mut executables = vec![(callee_program_id, program_account)];
|
||||||
None
|
if let Some(programdata) = programdata_executable {
|
||||||
};
|
executables.push(programdata);
|
||||||
let mut executable_accounts = vec![(callee_program_id, program_account)];
|
}
|
||||||
if let Some(programdata) = programdata_executable {
|
(
|
||||||
executable_accounts.push(programdata);
|
message,
|
||||||
}
|
executables,
|
||||||
|
accounts,
|
||||||
|
account_refs,
|
||||||
|
caller_privileges,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::deref_addrof)]
|
||||||
MessageProcessor::process_cross_program_instruction(
|
MessageProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executables,
|
||||||
&accounts,
|
&accounts,
|
||||||
&caller_privileges,
|
&caller_privileges,
|
||||||
invoke_context,
|
*(&mut *(invoke_context.borrow_mut())),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Copy results back to caller
|
// Copy results back to caller
|
||||||
|
|
||||||
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
{
|
||||||
let account = account.borrow();
|
let invoke_context = invoke_context.borrow();
|
||||||
if message.is_writable(i) && !account.executable {
|
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
||||||
account_ref.try_account_ref_mut()?.lamports = account.lamports;
|
let account = account.borrow();
|
||||||
account_ref.try_account_ref_mut()?.owner = account.owner;
|
if message.is_writable(i) && !account.executable {
|
||||||
if account_ref.data_len()? != account.data.len() && account_ref.data_len()? != 0 {
|
account_ref.try_account_ref_mut()?.lamports = account.lamports;
|
||||||
// Only support for `CreateAccount` at this time.
|
account_ref.try_account_ref_mut()?.owner = account.owner;
|
||||||
// Need a way to limit total realloc size across multiple CPI calls
|
if account_ref.data_len()? != account.data.len() && account_ref.data_len()? != 0
|
||||||
return Err(InstructionError::InvalidRealloc);
|
{
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
account_ref.try_account_ref_mut()?.data = account.data.clone();
|
||||||
}
|
}
|
||||||
account_ref.try_account_ref_mut()?.data = account.data.clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue