2019-03-23 21:12:27 -06:00
|
|
|
//! Defines a composable Instruction type and a memory-efficient CompiledInstruction.
|
|
|
|
|
2020-01-28 16:11:22 -08:00
|
|
|
use crate::{pubkey::Pubkey, short_vec, system_instruction::SystemError};
|
2019-03-24 22:51:56 -07:00
|
|
|
use bincode::serialize;
|
2019-03-23 21:12:27 -06:00
|
|
|
use serde::Serialize;
|
|
|
|
|
|
|
|
/// Reasons the runtime might have rejected an instruction.
|
2019-04-05 19:07:30 -06:00
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
2019-03-23 21:12:27 -06:00
|
|
|
pub enum InstructionError {
|
|
|
|
/// Deprecated! Use CustomError instead!
|
|
|
|
/// The program instruction returned an error
|
|
|
|
GenericError,
|
|
|
|
|
|
|
|
/// The arguments provided to a program instruction where invalid
|
|
|
|
InvalidArgument,
|
|
|
|
|
|
|
|
/// An instruction's data contents was invalid
|
|
|
|
InvalidInstructionData,
|
|
|
|
|
|
|
|
/// An account's data contents was invalid
|
|
|
|
InvalidAccountData,
|
|
|
|
|
|
|
|
/// An account's data was too small
|
|
|
|
AccountDataTooSmall,
|
|
|
|
|
2019-06-10 12:17:29 -07:00
|
|
|
/// An account's balance was too small to complete the instruction
|
|
|
|
InsufficientFunds,
|
|
|
|
|
2019-03-23 21:12:27 -06:00
|
|
|
/// The account did not have the expected program id
|
|
|
|
IncorrectProgramId,
|
|
|
|
|
|
|
|
/// A signature was required but not found
|
|
|
|
MissingRequiredSignature,
|
|
|
|
|
|
|
|
/// An initialize instruction was sent to an account that has already been initialized.
|
|
|
|
AccountAlreadyInitialized,
|
|
|
|
|
|
|
|
/// An attempt to operate on an account that hasn't been initialized.
|
|
|
|
UninitializedAccount,
|
|
|
|
|
|
|
|
/// Program's instruction lamport balance does not equal the balance after the instruction
|
|
|
|
UnbalancedInstruction,
|
|
|
|
|
|
|
|
/// Program modified an account's program id
|
|
|
|
ModifiedProgramId,
|
|
|
|
|
|
|
|
/// Program spent the lamports of an account that doesn't belong to it
|
|
|
|
ExternalAccountLamportSpend,
|
|
|
|
|
|
|
|
/// Program modified the data of an account that doesn't belong to it
|
|
|
|
ExternalAccountDataModified,
|
|
|
|
|
2019-11-05 09:38:35 -07:00
|
|
|
/// Read-only account modified lamports
|
|
|
|
ReadonlyLamportChange,
|
2019-06-10 20:50:02 -06:00
|
|
|
|
2019-11-05 09:38:35 -07:00
|
|
|
/// Read-only account modified data
|
|
|
|
ReadonlyDataModified,
|
2019-06-10 20:50:02 -06:00
|
|
|
|
2019-03-23 21:12:27 -06:00
|
|
|
/// An account was referenced more than once in a single instruction
|
2020-01-22 09:11:56 -08:00
|
|
|
// Deprecated, instructions can now contain duplicate accounts
|
2019-03-23 21:12:27 -06:00
|
|
|
DuplicateAccountIndex,
|
|
|
|
|
2019-07-29 15:29:20 -07:00
|
|
|
/// Executable bit on account changed, but shouldn't have
|
|
|
|
ExecutableModified,
|
|
|
|
|
2019-08-26 11:04:20 -07:00
|
|
|
/// Rent_epoch account changed, but shouldn't have
|
|
|
|
RentEpochModified,
|
|
|
|
|
2019-10-10 06:30:42 -06:00
|
|
|
/// The instruction expected additional account keys
|
|
|
|
NotEnoughAccountKeys,
|
|
|
|
|
2019-10-16 10:47:45 -07:00
|
|
|
/// A non-system program changed the size of the account data
|
|
|
|
AccountDataSizeChanged,
|
|
|
|
|
2020-01-20 15:27:36 -08:00
|
|
|
/// The instruction expected an executable account
|
2019-11-08 09:19:19 -08:00
|
|
|
AccountNotExecutable,
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
/// Failed to borrow a reference to an account, already borrowed
|
|
|
|
AccountBorrowFailed,
|
|
|
|
|
|
|
|
/// Account has an outstanding reference after a program's execution
|
|
|
|
AccountBorrowOutstanding,
|
|
|
|
|
|
|
|
/// The same account was multiply passed to an on-chain program's entrypoint, but the program
|
|
|
|
/// modified them differently. A program can only modify one instance of the account because
|
|
|
|
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
|
|
|
|
DuplicateAccountOutOfSync,
|
|
|
|
|
2019-03-23 21:12:27 -06:00
|
|
|
/// CustomError allows on-chain programs to implement program-specific error types and see
|
2019-04-11 11:41:12 -07:00
|
|
|
/// them returned by the Solana runtime. A CustomError may be any type that is represented
|
|
|
|
/// as or serialized to a u32 integer.
|
2019-09-06 16:05:01 -07:00
|
|
|
///
|
|
|
|
/// NOTE: u64 requires special serialization to avoid the loss of precision in JS clients and
|
|
|
|
/// so is not used for now.
|
2019-04-11 11:41:12 -07:00
|
|
|
CustomError(u32),
|
2020-01-30 09:47:22 -08:00
|
|
|
|
|
|
|
/// Like CustomError but the return value from the program conflicted with
|
|
|
|
/// a builtin error. The value held by this variant is the u32 error code
|
|
|
|
/// returned by the program but with the 30th bit cleared.
|
|
|
|
ConflictingError(u32),
|
2019-03-23 21:12:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl InstructionError {
|
|
|
|
pub fn new_result_with_negative_lamports() -> Self {
|
2019-04-11 11:41:12 -07:00
|
|
|
InstructionError::CustomError(SystemError::ResultWithNegativeLamports as u32)
|
2019-03-23 21:12:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-29 16:11:21 -06:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2019-03-27 17:06:50 -06:00
|
|
|
pub struct Instruction {
|
|
|
|
/// Pubkey of the instruction processor that executes this instruction
|
2019-07-01 18:34:22 -06:00
|
|
|
pub program_id: Pubkey,
|
2019-03-27 17:06:50 -06:00
|
|
|
/// Metadata for what accounts should be passed to the instruction processor
|
|
|
|
pub accounts: Vec<AccountMeta>,
|
|
|
|
/// Opaque data passed to the instruction processor
|
2019-03-23 21:12:27 -06:00
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
2019-03-27 17:06:50 -06:00
|
|
|
impl Instruction {
|
2019-07-01 18:34:22 -06:00
|
|
|
pub fn new<T: Serialize>(program_id: Pubkey, data: &T, accounts: Vec<AccountMeta>) -> Self {
|
2019-03-23 21:12:27 -06:00
|
|
|
let data = serialize(data).unwrap();
|
|
|
|
Self {
|
2019-07-01 18:34:22 -06:00
|
|
|
program_id,
|
2019-03-23 21:12:27 -06:00
|
|
|
data,
|
|
|
|
accounts,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Account metadata used to define Instructions
|
2019-03-29 16:11:21 -06:00
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
2019-03-23 21:12:27 -06:00
|
|
|
pub struct AccountMeta {
|
|
|
|
/// An account's public key
|
|
|
|
pub pubkey: Pubkey,
|
2019-11-25 21:09:57 -08:00
|
|
|
/// True if an Instruction requires a Transaction signature matching `pubkey`.
|
2019-03-23 21:12:27 -06:00
|
|
|
pub is_signer: bool,
|
2019-11-05 09:38:35 -07:00
|
|
|
/// True if the `pubkey` can be loaded as a read-write account.
|
|
|
|
pub is_writable: bool,
|
2019-03-23 21:12:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountMeta {
|
|
|
|
pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
|
2019-05-23 18:19:53 -04:00
|
|
|
Self {
|
|
|
|
pubkey,
|
|
|
|
is_signer,
|
2019-11-05 09:38:35 -07:00
|
|
|
is_writable: true,
|
2019-05-23 18:19:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-05 09:38:35 -07:00
|
|
|
pub fn new_readonly(pubkey: Pubkey, is_signer: bool) -> Self {
|
2019-05-23 18:19:53 -04:00
|
|
|
Self {
|
|
|
|
pubkey,
|
|
|
|
is_signer,
|
2019-11-05 09:38:35 -07:00
|
|
|
is_writable: false,
|
2019-05-23 18:19:53 -04:00
|
|
|
}
|
2019-03-23 21:12:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-18 15:43:47 -05:00
|
|
|
/// Trait for adding a signer Pubkey to an existing data structure
|
|
|
|
pub trait WithSigner {
|
|
|
|
/// Add a signer Pubkey
|
|
|
|
fn with_signer(self, signer: &Pubkey) -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WithSigner for Vec<AccountMeta> {
|
|
|
|
fn with_signer(mut self, signer: &Pubkey) -> Self {
|
|
|
|
for meta in self.iter_mut() {
|
|
|
|
// signer might already appear in parameters
|
|
|
|
if &meta.pubkey == signer {
|
|
|
|
meta.is_signer = true; // found it, we're done
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// signer wasn't in metas, append it after normal parameters
|
|
|
|
self.push(AccountMeta::new_readonly(*signer, true));
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 17:06:50 -06:00
|
|
|
/// An instruction to execute a program
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
2019-11-19 15:55:32 -07:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2019-03-27 17:06:50 -06:00
|
|
|
pub struct CompiledInstruction {
|
2019-05-22 18:23:16 -04:00
|
|
|
/// Index into the transaction keys array indicating the program account that executes this instruction
|
2019-07-01 18:34:22 -06:00
|
|
|
pub program_id_index: u8,
|
2019-03-27 17:06:50 -06:00
|
|
|
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program
|
2019-03-25 09:15:16 -06:00
|
|
|
#[serde(with = "short_vec")]
|
2019-03-27 17:06:50 -06:00
|
|
|
pub accounts: Vec<u8>,
|
|
|
|
/// The program input data
|
2019-03-25 09:15:16 -06:00
|
|
|
#[serde(with = "short_vec")]
|
2019-03-27 17:06:50 -06:00
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
2019-03-23 21:12:27 -06:00
|
|
|
|
|
|
|
impl CompiledInstruction {
|
2019-03-27 17:06:50 -06:00
|
|
|
pub fn new<T: Serialize>(program_ids_index: u8, data: &T, accounts: Vec<u8>) -> Self {
|
|
|
|
let data = serialize(data).unwrap();
|
|
|
|
Self {
|
2019-07-01 18:34:22 -06:00
|
|
|
program_id_index: program_ids_index,
|
2019-03-27 17:06:50 -06:00
|
|
|
data,
|
|
|
|
accounts,
|
|
|
|
}
|
|
|
|
}
|
2019-04-02 16:02:57 -06:00
|
|
|
|
|
|
|
pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey {
|
2019-07-01 18:34:22 -06:00
|
|
|
&program_ids[self.program_id_index as usize]
|
2019-04-02 16:02:57 -06:00
|
|
|
}
|
2019-03-23 21:12:27 -06:00
|
|
|
}
|
2019-11-18 15:43:47 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_account_meta_list_with_signer() {
|
|
|
|
let account_pubkey = Pubkey::new_rand();
|
|
|
|
let signer_pubkey = Pubkey::new_rand();
|
|
|
|
|
|
|
|
let account_meta = AccountMeta::new(account_pubkey, false);
|
|
|
|
let signer_account_meta = AccountMeta::new(signer_pubkey, false);
|
|
|
|
|
|
|
|
let metas = vec![].with_signer(&signer_pubkey);
|
|
|
|
assert_eq!(metas.len(), 1);
|
|
|
|
assert!(metas[0].is_signer);
|
|
|
|
|
|
|
|
let metas = vec![account_meta.clone()].with_signer(&signer_pubkey);
|
|
|
|
assert_eq!(metas.len(), 2);
|
|
|
|
assert!(!metas[0].is_signer);
|
|
|
|
assert!(metas[1].is_signer);
|
|
|
|
assert_eq!(metas[1].pubkey, signer_pubkey);
|
|
|
|
|
|
|
|
let metas = vec![signer_account_meta.clone()].with_signer(&signer_pubkey);
|
|
|
|
assert_eq!(metas.len(), 1);
|
|
|
|
assert!(metas[0].is_signer);
|
|
|
|
assert_eq!(metas[0].pubkey, signer_pubkey);
|
|
|
|
|
|
|
|
let metas = vec![account_meta, signer_account_meta].with_signer(&signer_pubkey);
|
|
|
|
assert_eq!(metas.len(), 2);
|
|
|
|
assert!(!metas[0].is_signer);
|
|
|
|
assert!(metas[1].is_signer);
|
|
|
|
assert_eq!(metas[1].pubkey, signer_pubkey);
|
|
|
|
}
|
|
|
|
}
|