Move Instruction into its own module

This commit is contained in:
Greg Fitzgerald
2019-03-23 21:12:27 -06:00
committed by Grimes
parent 68c35bfde6
commit acedf4ca5a
32 changed files with 202 additions and 187 deletions

149
sdk/src/instruction.rs Normal file
View File

@ -0,0 +1,149 @@
//! Defines a composable Instruction type and a memory-efficient CompiledInstruction.
use crate::pubkey::Pubkey;
use crate::shortvec::{deserialize_vec_bytes, encode_len, serialize_vec_bytes};
use crate::system_instruction::SystemError;
use bincode::{serialize, Error};
use serde::Serialize;
use std::io::{Cursor, Read, Write};
use std::mem::size_of;
/// Reasons the runtime might have rejected an instruction.
#[derive(Debug, PartialEq, Eq, Clone)]
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,
/// 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,
/// An account was referenced more than once in a single instruction
DuplicateAccountIndex,
/// CustomError allows on-chain programs to implement program-specific error types and see
/// them returned by the Solana runtime. A CustomError may be any type that is serialized
/// to a Vec of bytes, max length 32 bytes. Any CustomError Vec greater than this length will
/// be truncated by the runtime.
CustomError(Vec<u8>),
}
impl InstructionError {
pub fn new_result_with_negative_lamports() -> Self {
let serialized_error =
bincode::serialize(&SystemError::ResultWithNegativeLamports).unwrap();
InstructionError::CustomError(serialized_error)
}
}
/// An instruction to execute a program
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GenericInstruction<P, Q> {
/// Index into the transaction program ids array indicating the program account that executes this instruction
pub program_ids_index: P,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program
pub accounts: Vec<Q>,
/// The program input data
pub data: Vec<u8>,
}
impl<P, Q> GenericInstruction<P, Q> {
pub fn new<T: Serialize>(program_ids_index: P, data: &T, accounts: Vec<Q>) -> Self {
let data = serialize(data).unwrap();
Self {
program_ids_index,
data,
accounts,
}
}
}
/// Account metadata used to define Instructions
#[derive(Debug)]
pub struct AccountMeta {
/// An account's public key
pub pubkey: Pubkey,
/// True if an Instruciton requires a Transaction signature matching `pubkey`.
pub is_signer: bool,
}
impl AccountMeta {
pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
Self { pubkey, is_signer }
}
}
pub type Instruction = GenericInstruction<Pubkey, AccountMeta>;
pub type CompiledInstruction = GenericInstruction<u8, u8>;
impl CompiledInstruction {
pub fn serialize_with(mut writer: &mut Cursor<&mut [u8]>, ix: &Self) -> Result<(), Error> {
writer.write_all(&[ix.program_ids_index])?;
serialize_vec_bytes(&mut writer, &ix.accounts[..])?;
serialize_vec_bytes(&mut writer, &ix.data[..])?;
Ok(())
}
pub fn deserialize_from(mut reader: &mut Cursor<&[u8]>) -> Result<Self, Error> {
let mut buf = [0];
reader.read_exact(&mut buf)?;
let program_ids_index = buf[0];
let accounts = deserialize_vec_bytes(&mut reader)?;
let data = deserialize_vec_bytes(&mut reader)?;
Ok(CompiledInstruction {
program_ids_index,
accounts,
data,
})
}
pub fn serialized_size(&self) -> Result<u64, Error> {
let mut buf = [0; size_of::<u64>() + 1];
let mut wr = Cursor::new(&mut buf[..]);
let mut size = size_of::<u8>();
let len = self.accounts.len();
encode_len(&mut wr, len)?;
size += wr.position() as usize + (len * size_of::<u8>());
let len = self.data.len();
wr.set_position(0);
encode_len(&mut wr, len)?;
size += wr.position() as usize + (len * size_of::<u8>());
Ok(size as u64)
}
}

View File

@ -2,6 +2,7 @@ pub mod account;
pub mod bpf_loader;
pub mod genesis_block;
pub mod hash;
pub mod instruction;
pub mod loader_instruction;
pub mod native_loader;
pub mod native_program;

View File

@ -1,5 +1,5 @@
use crate::instruction::{AccountMeta, Instruction};
use crate::pubkey::Pubkey;
use crate::transaction::{AccountMeta, Instruction};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum LoaderInstruction {

View File

@ -1,6 +1,6 @@
use crate::account::KeyedAccount;
use crate::instruction::InstructionError;
use crate::pubkey::Pubkey;
use crate::transaction::InstructionError;
// All native programs export a symbol named process()
pub const ENTRYPOINT: &str = "process";
@ -24,7 +24,7 @@ macro_rules! solana_entrypoint(
keyed_accounts: &mut [solana_sdk::account::KeyedAccount],
data: &[u8],
tick_height: u64
) -> Result<(), solana_sdk::transaction::InstructionError> {
) -> Result<(), solana_sdk::instruction::InstructionError> {
$entrypoint(program_id, keyed_accounts, data, tick_height)
}
)
@ -40,7 +40,7 @@ macro_rules! process_instruction_entrypoint(
keyed_accounts: &mut [solana_sdk::account::KeyedAccount],
data: &[u8],
tick_height: u64,
) -> Result<(), solana_sdk::transaction::InstructionError> {
) -> Result<(), solana_sdk::instruction::InstructionError> {
solana_logger::setup();
log::trace!("process_instruction: {:?}", data);

View File

@ -1,8 +1,9 @@
//! A library for building scripts and compiling them into transactions.
use crate::hash::Hash;
use crate::instruction::{CompiledInstruction, Instruction};
use crate::pubkey::Pubkey;
use crate::transaction::{CompiledInstruction, Instruction, Transaction};
use crate::transaction::Transaction;
use itertools::Itertools;
fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
@ -104,8 +105,8 @@ impl Script {
#[cfg(test)]
mod tests {
use super::*;
use crate::instruction::AccountMeta;
use crate::signature::{Keypair, KeypairUtil};
use crate::transaction::AccountMeta;
#[test]
fn test_transaction_builder_unique_program_ids() {

View File

@ -1,6 +1,6 @@
use crate::instruction::{AccountMeta, Instruction};
use crate::pubkey::Pubkey;
use crate::system_program;
use crate::transaction::{AccountMeta, Instruction};
#[derive(Serialize, Debug, Clone, PartialEq)]
pub enum SystemError {

View File

@ -1,162 +1,19 @@
//! The `transaction` module provides functionality for creating log transactions.
//! Defines a Transaction type to package an atomic sequence of instructions.
use crate::hash::{Hash, Hasher};
use crate::instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError};
use crate::packet::PACKET_DATA_SIZE;
use crate::pubkey::Pubkey;
use crate::script::Script;
use crate::shortvec::{
deserialize_vec_bytes, deserialize_vec_with, encode_len, serialize_vec_bytes,
serialize_vec_with,
};
use crate::shortvec::{deserialize_vec_with, encode_len, serialize_vec_with};
use crate::signature::{KeypairUtil, Signature};
use crate::system_instruction::SystemError;
use bincode::{serialize, Error};
use bincode::Error;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde::{Deserialize, Serialize, Serializer};
use std::fmt;
use std::io::{Cursor, Read, Write};
use std::mem::size_of;
/// Reasons the runtime might have rejected an instruction.
#[derive(Debug, PartialEq, Eq, Clone)]
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,
/// 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,
/// An account was referenced more than once in a single instruction
DuplicateAccountIndex,
/// CustomError allows on-chain programs to implement program-specific error types and see
/// them returned by the Solana runtime. A CustomError may be any type that is serialized
/// to a Vec of bytes, max length 32 bytes. Any CustomError Vec greater than this length will
/// be truncated by the runtime.
CustomError(Vec<u8>),
}
impl InstructionError {
pub fn new_result_with_negative_lamports() -> Self {
let serialized_error =
bincode::serialize(&SystemError::ResultWithNegativeLamports).unwrap();
InstructionError::CustomError(serialized_error)
}
}
/// An instruction to execute a program
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GenericInstruction<P, Q> {
/// Index into the transaction program ids array indicating the program account that executes this instruction
pub program_ids_index: P,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program
pub accounts: Vec<Q>,
/// The program input data
pub data: Vec<u8>,
}
impl<P, Q> GenericInstruction<P, Q> {
pub fn new<T: Serialize>(program_ids_index: P, data: &T, accounts: Vec<Q>) -> Self {
let data = serialize(data).unwrap();
Self {
program_ids_index,
data,
accounts,
}
}
}
/// Account metadata used to define Instructions
#[derive(Debug)]
pub struct AccountMeta {
/// An account's public key
pub pubkey: Pubkey,
/// True if an Instruciton requires a Transaction signature matching `pubkey`.
pub is_signer: bool,
}
impl AccountMeta {
pub fn new(pubkey: Pubkey, is_signer: bool) -> Self {
Self { pubkey, is_signer }
}
}
pub type Instruction = GenericInstruction<Pubkey, AccountMeta>;
pub type CompiledInstruction = GenericInstruction<u8, u8>;
impl CompiledInstruction {
pub fn serialize_with(mut writer: &mut Cursor<&mut [u8]>, ix: &Self) -> Result<(), Error> {
writer.write_all(&[ix.program_ids_index])?;
serialize_vec_bytes(&mut writer, &ix.accounts[..])?;
serialize_vec_bytes(&mut writer, &ix.data[..])?;
Ok(())
}
pub fn deserialize_from(mut reader: &mut Cursor<&[u8]>) -> Result<Self, Error> {
let mut buf = [0];
reader.read_exact(&mut buf)?;
let program_ids_index = buf[0];
let accounts = deserialize_vec_bytes(&mut reader)?;
let data = deserialize_vec_bytes(&mut reader)?;
Ok(CompiledInstruction {
program_ids_index,
accounts,
data,
})
}
pub fn serialized_size(&self) -> Result<u64, Error> {
let mut buf = [0; size_of::<u64>() + 1];
let mut wr = Cursor::new(&mut buf[..]);
let mut size = size_of::<u8>();
let len = self.accounts.len();
encode_len(&mut wr, len)?;
size += wr.position() as usize + (len * size_of::<u8>());
let len = self.data.len();
wr.set_position(0);
encode_len(&mut wr, len)?;
size += wr.position() as usize + (len * size_of::<u8>());
Ok(size as u64)
}
}
/// Reasons a transaction might be rejected.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TransactionError {
@ -553,7 +410,7 @@ impl<'de> Deserialize<'de> for Transaction {
mod tests {
use super::*;
use crate::signature::Keypair;
use bincode::deserialize;
use bincode::{deserialize, serialize};
#[test]
fn test_refs() {