#![allow(clippy::integer_arithmetic)]
use {
crate::{pubkey::Pubkey, sanitize::Sanitize, short_vec, wasm_bindgen},
bincode::serialize,
borsh::BorshSerialize,
serde::Serialize,
thiserror::Error,
};
#[derive(
Serialize, Deserialize, Debug, Error, PartialEq, Eq, Clone, AbiExample, AbiEnumVisitor,
)]
pub enum InstructionError {
#[error("generic instruction error")]
GenericError,
#[error("invalid program argument")]
InvalidArgument,
#[error("invalid instruction data")]
InvalidInstructionData,
#[error("invalid account data for instruction")]
InvalidAccountData,
#[error("account data too small for instruction")]
AccountDataTooSmall,
#[error("insufficient funds for instruction")]
InsufficientFunds,
#[error("incorrect program id for instruction")]
IncorrectProgramId,
#[error("missing required signature for instruction")]
MissingRequiredSignature,
#[error("instruction requires an uninitialized account")]
AccountAlreadyInitialized,
#[error("instruction requires an initialized account")]
UninitializedAccount,
#[error("sum of account balances before and after instruction do not match")]
UnbalancedInstruction,
#[error("instruction illegally modified the program id of an account")]
ModifiedProgramId,
#[error("instruction spent from the balance of an account it does not own")]
ExternalAccountLamportSpend,
#[error("instruction modified data of an account it does not own")]
ExternalAccountDataModified,
#[error("instruction changed the balance of a read-only account")]
ReadonlyLamportChange,
#[error("instruction modified data of a read-only account")]
ReadonlyDataModified,
#[error("instruction contains duplicate accounts")]
DuplicateAccountIndex,
#[error("instruction changed executable bit of an account")]
ExecutableModified,
#[error("instruction modified rent epoch of an account")]
RentEpochModified,
#[error("insufficient account keys for instruction")]
NotEnoughAccountKeys,
#[error("program other than the account's owner changed the size of the account data")]
AccountDataSizeChanged,
#[error("instruction expected an executable account")]
AccountNotExecutable,
#[error("instruction tries to borrow reference for an account which is already borrowed")]
AccountBorrowFailed,
#[error("instruction left account with an outstanding borrowed reference")]
AccountBorrowOutstanding,
#[error("instruction modifications of multiply-passed account differ")]
DuplicateAccountOutOfSync,
#[error("custom program error: {0:#x}")]
Custom(u32),
#[error("program returned invalid error code")]
InvalidError,
#[error("instruction changed executable accounts data")]
ExecutableDataModified,
#[error("instruction changed the balance of a executable account")]
ExecutableLamportChange,
#[error("executable accounts must be rent exempt")]
ExecutableAccountNotRentExempt,
#[error("Unsupported program id")]
UnsupportedProgramId,
#[error("Cross-program invocation call depth too deep")]
CallDepth,
#[error("An account required by the instruction is missing")]
MissingAccount,
#[error("Cross-program invocation reentrancy not allowed for this instruction")]
ReentrancyNotAllowed,
#[error("Length of the seed is too long for address generation")]
MaxSeedLengthExceeded,
#[error("Provided seeds do not result in a valid address")]
InvalidSeeds,
#[error("Failed to reallocate account data")]
InvalidRealloc,
#[error("Computational budget exceeded")]
ComputationalBudgetExceeded,
#[error("Cross-program invocation with unauthorized signer or writable account")]
PrivilegeEscalation,
#[error("Failed to create program execution environment")]
ProgramEnvironmentSetupFailure,
#[error("Program failed to complete")]
ProgramFailedToComplete,
#[error("Program failed to compile")]
ProgramFailedToCompile,
#[error("Account is immutable")]
Immutable,
#[error("Incorrect authority provided")]
IncorrectAuthority,
#[error("Failed to serialize or deserialize account data: {0}")]
BorshIoError(String),
#[error("An account does not have enough lamports to be rent-exempt")]
AccountNotRentExempt,
#[error("Invalid account owner")]
InvalidAccountOwner,
#[error("Program arithmetic overflowed")]
ArithmeticOverflow,
#[error("Unsupported sysvar")]
UnsupportedSysvar,
#[error("Provided owner is not allowed")]
IllegalOwner,
#[error("Accounts data allocations exceeded the maximum allowed per transaction")]
MaxAccountsDataAllocationsExceeded,
#[error("Max accounts exceeded")]
MaxAccountsExceeded,
#[error("Max instruction trace length exceeded")]
MaxInstructionTraceLengthExceeded,
#[error("Builtin programs must consume compute units")]
BuiltinProgramsMustConsumeComputeUnits,
}
#[wasm_bindgen]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Instruction {
#[wasm_bindgen(skip)]
pub program_id: Pubkey,
#[wasm_bindgen(skip)]
pub accounts: Vec<AccountMeta>,
#[wasm_bindgen(skip)]
pub data: Vec<u8>,
}
impl Instruction {
pub fn new_with_borsh<T: BorshSerialize>(
program_id: Pubkey,
data: &T,
accounts: Vec<AccountMeta>,
) -> Self {
let data = data.try_to_vec().unwrap();
Self {
program_id,
accounts,
data,
}
}
pub fn new_with_bincode<T: Serialize>(
program_id: Pubkey,
data: &T,
accounts: Vec<AccountMeta>,
) -> Self {
let data = serialize(data).unwrap();
Self {
program_id,
accounts,
data,
}
}
pub fn new_with_bytes(program_id: Pubkey, data: &[u8], accounts: Vec<AccountMeta>) -> Self {
Self {
program_id,
accounts,
data: data.to_vec(),
}
}
#[deprecated(
since = "1.6.0",
note = "Please use another Instruction constructor instead, such as `Instruction::new_with_borsh`"
)]
pub fn new<T: Serialize>(program_id: Pubkey, data: &T, accounts: Vec<AccountMeta>) -> Self {
Self::new_with_bincode(program_id, data, accounts)
}
}
#[doc(hidden)]
pub fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
a.checked_add(b).ok_or(InstructionError::InsufficientFunds)
}
#[repr(C)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct AccountMeta {
pub pubkey: Pubkey,
pub is_signer: bool,
pub is_writable: bool,
}
impl AccountMeta {
pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
Self {
pubkey,
is_signer,
is_writable: true,
}
}
pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
Self {
pubkey,
is_signer,
is_writable: false,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
#[serde(rename_all = "camelCase")]
pub struct CompiledInstruction {
pub program_id_index: u8,
#[serde(with = "short_vec")]
pub accounts: Vec<u8>,
#[serde(with = "short_vec")]
pub data: Vec<u8>,
}
impl Sanitize for CompiledInstruction {}
impl CompiledInstruction {
pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self {
let data = serialize(data).unwrap();
Self {
program_id_index: program_ids_index,
accounts,
data,
}
}
pub fn new_from_raw_parts(program_id_index: u8, data: Vec<u8>, accounts: Vec<u8>) -> Self {
Self {
program_id_index,
accounts,
data,
}
}
pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey {
&program_ids[self.program_id_index as usize]
}
}
#[repr(C)]
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
pub struct ProcessedSiblingInstruction {
pub data_len: u64,
pub accounts_len: u64,
}
pub fn get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
#[cfg(target_os = "solana")]
{
let mut meta = ProcessedSiblingInstruction::default();
let mut program_id = Pubkey::default();
if 1 == unsafe {
crate::syscalls::sol_get_processed_sibling_instruction(
index as u64,
&mut meta,
&mut program_id,
&mut u8::default(),
&mut AccountMeta::default(),
)
} {
let mut data = Vec::new();
let mut accounts = Vec::new();
data.resize_with(meta.data_len as usize, u8::default);
accounts.resize_with(meta.accounts_len as usize, AccountMeta::default);
let _ = unsafe {
crate::syscalls::sol_get_processed_sibling_instruction(
index as u64,
&mut meta,
&mut program_id,
data.as_mut_ptr(),
accounts.as_mut_ptr(),
)
};
Some(Instruction::new_with_bytes(program_id, &data, accounts))
} else {
None
}
}
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_get_processed_sibling_instruction(index)
}
pub const TRANSACTION_LEVEL_STACK_HEIGHT: usize = 1;
pub fn get_stack_height() -> usize {
#[cfg(target_os = "solana")]
unsafe {
crate::syscalls::sol_get_stack_height() as usize
}
#[cfg(not(target_os = "solana"))]
{
crate::program_stubs::sol_get_stack_height() as usize
}
}
#[test]
fn test_account_meta_layout() {
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
struct AccountMetaRust {
pub pubkey: Pubkey,
pub is_signer: bool,
pub is_writable: bool,
}
let account_meta_rust = AccountMetaRust::default();
let base_rust_addr = &account_meta_rust as *const _ as u64;
let pubkey_rust_addr = &account_meta_rust.pubkey as *const _ as u64;
let is_signer_rust_addr = &account_meta_rust.is_signer as *const _ as u64;
let is_writable_rust_addr = &account_meta_rust.is_writable as *const _ as u64;
let account_meta_c = AccountMeta::default();
let base_c_addr = &account_meta_c as *const _ as u64;
let pubkey_c_addr = &account_meta_c.pubkey as *const _ as u64;
let is_signer_c_addr = &account_meta_c.is_signer as *const _ as u64;
let is_writable_c_addr = &account_meta_c.is_writable as *const _ as u64;
assert_eq!(
std::mem::size_of::<AccountMetaRust>(),
std::mem::size_of::<AccountMeta>()
);
assert_eq!(
pubkey_rust_addr - base_rust_addr,
pubkey_c_addr - base_c_addr
);
assert_eq!(
is_signer_rust_addr - base_rust_addr,
is_signer_c_addr - base_c_addr
);
assert_eq!(
is_writable_rust_addr - base_rust_addr,
is_writable_c_addr - base_c_addr
);
}