2018-11-16 08:04:46 -08:00
|
|
|
//! The `transaction` module provides functionality for creating log transactions.
|
|
|
|
|
2018-12-14 20:39:10 -08:00
|
|
|
use crate::hash::{Hash, Hasher};
|
2019-03-13 14:37:24 -06:00
|
|
|
use crate::native_program::ProgramError;
|
2019-01-24 21:14:15 -08:00
|
|
|
use crate::packet::PACKET_DATA_SIZE;
|
2018-12-14 20:39:10 -08:00
|
|
|
use crate::pubkey::Pubkey;
|
2019-01-24 21:14:15 -08:00
|
|
|
use crate::shortvec::{
|
|
|
|
deserialize_vec_bytes, deserialize_vec_with, encode_len, serialize_vec_bytes,
|
|
|
|
serialize_vec_with,
|
|
|
|
};
|
2019-03-14 14:15:00 -06:00
|
|
|
use crate::signature::{KeypairUtil, Signature};
|
2019-03-13 14:37:24 -06:00
|
|
|
use crate::system_instruction::SystemError;
|
2019-03-14 14:15:00 -06:00
|
|
|
use crate::transaction_builder::{BuilderInstruction, TransactionBuilder};
|
2019-01-24 21:14:15 -08:00
|
|
|
use bincode::{serialize, Error};
|
|
|
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
|
|
use serde::{Deserialize, Serialize, Serializer};
|
|
|
|
use std::fmt;
|
|
|
|
use std::io::{Cursor, Read, Write};
|
2018-11-16 08:04:46 -08:00
|
|
|
use std::mem::size_of;
|
|
|
|
|
2019-03-13 14:37:24 -06:00
|
|
|
/// Reasons the runtime might have rejected an instruction.
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum InstructionError {
|
|
|
|
/// Executing the instruction produced an error.
|
|
|
|
ProgramError(ProgramError),
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
2019-03-14 10:48:27 -06:00
|
|
|
/// Program modified the data of an account that doesn't belong to it
|
|
|
|
ExternalAccountDataModified,
|
2019-03-14 15:32:12 -06:00
|
|
|
|
|
|
|
/// An account was referenced more than once in a single instruction
|
|
|
|
DuplicateAccountIndex,
|
2019-03-13 14:37:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl InstructionError {
|
|
|
|
pub fn new_result_with_negative_lamports() -> Self {
|
|
|
|
let serialized_error =
|
|
|
|
bincode::serialize(&SystemError::ResultWithNegativeLamports).unwrap();
|
|
|
|
InstructionError::ProgramError(ProgramError::CustomError(serialized_error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 19:19:56 -08:00
|
|
|
/// An instruction to execute a program
|
2018-11-16 08:04:46 -08:00
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
2019-02-28 02:19:04 -07:00
|
|
|
pub struct Instruction<P, Q> {
|
2019-01-04 19:19:56 -08:00
|
|
|
/// Index into the transaction program ids array indicating the program account that executes this instruction
|
2019-02-28 02:19:04 -07:00
|
|
|
pub program_ids_index: P,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program
|
2019-02-28 02:19:04 -07:00
|
|
|
pub accounts: Vec<Q>,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// The program input data
|
2019-03-14 10:48:27 -06:00
|
|
|
pub data: Vec<u8>,
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
2019-02-28 02:19:04 -07:00
|
|
|
impl<P, Q> Instruction<P, Q> {
|
2019-03-14 10:48:27 -06:00
|
|
|
pub fn new<T: Serialize>(program_ids_index: P, data: &T, accounts: Vec<Q>) -> Self {
|
|
|
|
let data = serialize(data).unwrap();
|
2019-02-28 02:19:04 -07:00
|
|
|
Self {
|
2018-11-16 08:04:46 -08:00
|
|
|
program_ids_index,
|
2019-03-14 10:48:27 -06:00
|
|
|
data,
|
2018-11-16 08:04:46 -08:00
|
|
|
accounts,
|
|
|
|
}
|
|
|
|
}
|
2019-02-28 02:19:04 -07:00
|
|
|
}
|
2019-01-24 21:14:15 -08:00
|
|
|
|
2019-02-28 02:19:04 -07:00
|
|
|
impl Instruction<u8, u8> {
|
|
|
|
pub fn serialize_with(mut writer: &mut Cursor<&mut [u8]>, ix: &Self) -> Result<(), Error> {
|
2019-01-24 21:14:15 -08:00
|
|
|
writer.write_all(&[ix.program_ids_index])?;
|
|
|
|
serialize_vec_bytes(&mut writer, &ix.accounts[..])?;
|
2019-03-14 10:48:27 -06:00
|
|
|
serialize_vec_bytes(&mut writer, &ix.data[..])?;
|
2019-01-24 21:14:15 -08:00
|
|
|
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)?;
|
2019-03-14 10:48:27 -06:00
|
|
|
let data = deserialize_vec_bytes(&mut reader)?;
|
2019-01-24 21:14:15 -08:00
|
|
|
Ok(Instruction {
|
|
|
|
program_ids_index,
|
|
|
|
accounts,
|
2019-03-14 10:48:27 -06:00
|
|
|
data,
|
2019-01-24 21:14:15 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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>());
|
|
|
|
|
2019-03-14 10:48:27 -06:00
|
|
|
let len = self.data.len();
|
2019-01-24 21:14:15 -08:00
|
|
|
wr.set_position(0);
|
|
|
|
encode_len(&mut wr, len)?;
|
|
|
|
size += wr.position() as usize + (len * size_of::<u8>());
|
|
|
|
|
|
|
|
Ok(size as u64)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
2019-03-13 14:37:24 -06:00
|
|
|
/// Reasons a transaction might be rejected.
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum TransactionError {
|
|
|
|
/// This Pubkey is being processed in another transaction
|
|
|
|
AccountInUse,
|
|
|
|
|
|
|
|
/// Pubkey appears twice in the same transaction, typically in a pay-to-self
|
|
|
|
/// transaction.
|
|
|
|
AccountLoadedTwice,
|
|
|
|
|
|
|
|
/// Attempt to debit from `Pubkey`, but no found no record of a prior credit.
|
|
|
|
AccountNotFound,
|
|
|
|
|
|
|
|
/// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction
|
|
|
|
InsufficientFundsForFee,
|
|
|
|
|
|
|
|
/// The bank has seen `Signature` before. This can occur under normal operation
|
|
|
|
/// when a UDP packet is duplicated, as a user error from a client not updating
|
|
|
|
/// its `recent_blockhash`, or as a double-spend attack.
|
|
|
|
DuplicateSignature,
|
|
|
|
|
|
|
|
/// The bank has not seen the given `recent_blockhash` or the transaction is too old and
|
|
|
|
/// the `recent_blockhash` has been discarded.
|
|
|
|
BlockhashNotFound,
|
|
|
|
|
|
|
|
/// The program returned an error
|
|
|
|
InstructionError(u8, InstructionError),
|
|
|
|
|
|
|
|
/// Loader call chain too deep
|
|
|
|
CallChainTooDeep,
|
|
|
|
|
|
|
|
/// Transaction has a fee but has no signature present
|
|
|
|
MissingSignatureForFee,
|
|
|
|
}
|
|
|
|
|
2018-11-16 08:04:46 -08:00
|
|
|
/// An atomic transaction
|
2019-01-24 21:14:15 -08:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2018-11-16 08:04:46 -08:00
|
|
|
pub struct Transaction {
|
2019-03-02 10:25:16 -08:00
|
|
|
/// A set of digital signatures of `account_keys`, `program_ids`, `recent_blockhash`, `fee` and `instructions`, signed by the first
|
2018-11-16 08:04:46 -08:00
|
|
|
/// signatures.len() keys of account_keys
|
|
|
|
pub signatures: Vec<Signature>,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// All the account keys used by this transaction
|
2018-11-16 08:04:46 -08:00
|
|
|
pub account_keys: Vec<Pubkey>,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// The id of a recent ledger entry.
|
2019-03-02 10:25:16 -08:00
|
|
|
pub recent_blockhash: Hash,
|
2019-03-05 16:28:14 -08:00
|
|
|
/// The number of lamports paid for processing and storing of this transaction.
|
2018-11-16 08:04:46 -08:00
|
|
|
pub fee: u64,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// All the program id keys used to execute this transaction's instructions
|
2018-11-16 08:04:46 -08:00
|
|
|
pub program_ids: Vec<Pubkey>,
|
2019-01-04 19:19:56 -08:00
|
|
|
/// Programs that will be executed in sequence and committed in one atomic transaction if all
|
2018-11-16 08:04:46 -08:00
|
|
|
/// succeed.
|
2019-02-28 02:19:04 -07:00
|
|
|
pub instructions: Vec<Instruction<u8, u8>>,
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Transaction {
|
2019-01-29 16:57:48 -07:00
|
|
|
pub fn new<S: Serialize, T: KeypairUtil>(
|
|
|
|
from_keypair: &T,
|
2018-11-16 08:04:46 -08:00
|
|
|
transaction_keys: &[Pubkey],
|
2019-03-09 19:28:43 -08:00
|
|
|
program_id: &Pubkey,
|
2019-03-14 10:48:27 -06:00
|
|
|
data: &S,
|
2019-03-02 10:25:16 -08:00
|
|
|
recent_blockhash: Hash,
|
2018-11-16 08:04:46 -08:00
|
|
|
fee: u64,
|
|
|
|
) -> Self {
|
2019-03-14 14:12:40 -06:00
|
|
|
let mut transaction = Self::new_unsigned(
|
|
|
|
&from_keypair.pubkey(),
|
2018-11-16 08:04:46 -08:00
|
|
|
transaction_keys,
|
2019-03-14 14:12:40 -06:00
|
|
|
program_id,
|
|
|
|
data,
|
|
|
|
Hash::default(),
|
2018-11-16 08:04:46 -08:00
|
|
|
fee,
|
2019-03-14 14:12:40 -06:00
|
|
|
);
|
2019-03-14 15:55:28 -06:00
|
|
|
transaction.sign(&[from_keypair], recent_blockhash);
|
2019-03-14 14:12:40 -06:00
|
|
|
transaction
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
2019-01-05 12:57:52 -08:00
|
|
|
pub fn new_unsigned<T: Serialize>(
|
|
|
|
from_pubkey: &Pubkey,
|
|
|
|
transaction_keys: &[Pubkey],
|
2019-03-09 19:28:43 -08:00
|
|
|
program_id: &Pubkey,
|
2019-03-14 10:48:27 -06:00
|
|
|
data: &T,
|
2019-03-02 10:25:16 -08:00
|
|
|
recent_blockhash: Hash,
|
2019-01-05 12:57:52 -08:00
|
|
|
fee: u64,
|
|
|
|
) -> Self {
|
2019-03-14 14:15:00 -06:00
|
|
|
let mut account_keys = vec![(*from_pubkey, true)];
|
|
|
|
for pubkey in transaction_keys {
|
|
|
|
account_keys.push((*pubkey, false));
|
|
|
|
}
|
|
|
|
let instruction = BuilderInstruction::new(*program_id, data, account_keys);
|
|
|
|
let mut transaction = TransactionBuilder::new(fee).push(instruction).compile();
|
|
|
|
transaction.recent_blockhash = recent_blockhash;
|
|
|
|
transaction
|
2019-01-05 12:57:52 -08:00
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create a signed transaction
|
|
|
|
/// * `from_keypair` - The key used to sign the transaction. This key is stored as keys[0]
|
|
|
|
/// * `account_keys` - The keys for the transaction. These are the program state
|
2019-03-05 16:28:14 -08:00
|
|
|
/// instances or lamport recipient keys.
|
2019-03-02 10:25:16 -08:00
|
|
|
/// * `recent_blockhash` - The PoH hash.
|
2018-11-16 08:04:46 -08:00
|
|
|
/// * `fee` - The transaction fee.
|
|
|
|
/// * `program_ids` - The keys that identify programs used in the `instruction` vector.
|
|
|
|
/// * `instructions` - The programs and their arguments that the transaction will execute atomically
|
2019-01-29 16:57:48 -07:00
|
|
|
pub fn new_with_instructions<T: KeypairUtil>(
|
|
|
|
from_keypairs: &[&T],
|
2018-11-16 08:04:46 -08:00
|
|
|
keys: &[Pubkey],
|
2019-03-02 10:25:16 -08:00
|
|
|
recent_blockhash: Hash,
|
2018-11-16 08:04:46 -08:00
|
|
|
fee: u64,
|
|
|
|
program_ids: Vec<Pubkey>,
|
2019-02-28 02:19:04 -07:00
|
|
|
instructions: Vec<Instruction<u8, u8>>,
|
2018-11-16 08:04:46 -08:00
|
|
|
) -> Self {
|
|
|
|
let mut account_keys: Vec<_> = from_keypairs
|
|
|
|
.iter()
|
|
|
|
.map(|keypair| keypair.pubkey())
|
|
|
|
.collect();
|
|
|
|
account_keys.extend_from_slice(keys);
|
|
|
|
let mut tx = Transaction {
|
2019-03-14 15:32:12 -06:00
|
|
|
signatures: Vec::with_capacity(from_keypairs.len()),
|
2018-11-16 08:04:46 -08:00
|
|
|
account_keys,
|
2019-03-02 10:25:16 -08:00
|
|
|
recent_blockhash: Hash::default(),
|
2018-11-16 08:04:46 -08:00
|
|
|
fee,
|
|
|
|
program_ids,
|
|
|
|
instructions,
|
|
|
|
};
|
2019-03-14 15:55:28 -06:00
|
|
|
tx.sign(from_keypairs, recent_blockhash);
|
2018-11-16 08:04:46 -08:00
|
|
|
tx
|
|
|
|
}
|
2019-03-14 10:48:27 -06:00
|
|
|
pub fn data(&self, instruction_index: usize) -> &[u8] {
|
|
|
|
&self.instructions[instruction_index].data
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
2019-02-01 08:36:35 -07:00
|
|
|
|
2018-11-16 08:04:46 -08:00
|
|
|
fn key_index(&self, instruction_index: usize, accounts_index: usize) -> Option<usize> {
|
|
|
|
self.instructions
|
|
|
|
.get(instruction_index)
|
|
|
|
.and_then(|instruction| instruction.accounts.get(accounts_index))
|
|
|
|
.map(|&account_keys_index| account_keys_index as usize)
|
|
|
|
}
|
|
|
|
pub fn key(&self, instruction_index: usize, accounts_index: usize) -> Option<&Pubkey> {
|
|
|
|
self.key_index(instruction_index, accounts_index)
|
|
|
|
.and_then(|account_keys_index| self.account_keys.get(account_keys_index))
|
|
|
|
}
|
2018-11-29 12:32:16 -08:00
|
|
|
pub fn signer_key(&self, instruction_index: usize, accounts_index: usize) -> Option<&Pubkey> {
|
2018-11-16 08:04:46 -08:00
|
|
|
match self.key_index(instruction_index, accounts_index) {
|
|
|
|
None => None,
|
|
|
|
Some(signature_index) => {
|
|
|
|
if signature_index >= self.signatures.len() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
self.account_keys.get(signature_index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn program_id(&self, instruction_index: usize) -> &Pubkey {
|
|
|
|
let program_ids_index = self.instructions[instruction_index].program_ids_index;
|
|
|
|
&self.program_ids[program_ids_index as usize]
|
|
|
|
}
|
|
|
|
/// Get the transaction data to sign.
|
2019-01-25 23:41:20 -07:00
|
|
|
pub fn message(&self) -> Vec<u8> {
|
2019-01-24 21:14:15 -08:00
|
|
|
let mut buf = vec![0u8; PACKET_DATA_SIZE];
|
|
|
|
let mut wr = Cursor::new(&mut buf[..]);
|
|
|
|
serialize_vec_with(&mut wr, &self.account_keys, Transaction::serialize_pubkey)
|
|
|
|
.expect("serialize account_keys");
|
2019-03-02 10:25:16 -08:00
|
|
|
wr.write_all(self.recent_blockhash.as_ref())
|
|
|
|
.expect("serialize recent_blockhash");
|
2019-01-24 21:14:15 -08:00
|
|
|
wr.write_u64::<LittleEndian>(self.fee)
|
|
|
|
.expect("serialize fee");
|
|
|
|
serialize_vec_with(&mut wr, &self.program_ids, Transaction::serialize_pubkey)
|
|
|
|
.expect("serialize program_ids");
|
|
|
|
serialize_vec_with(&mut wr, &self.instructions, Instruction::serialize_with)
|
|
|
|
.expect("serialize instructions");
|
|
|
|
let len = wr.position() as usize;
|
|
|
|
wr.into_inner()[..len].to_vec()
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sign this transaction.
|
2019-03-14 15:55:28 -06:00
|
|
|
pub fn sign_unchecked<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
|
2019-03-02 10:25:16 -08:00
|
|
|
self.recent_blockhash = recent_blockhash;
|
2019-01-25 23:41:20 -07:00
|
|
|
let message = self.message();
|
2018-11-16 08:04:46 -08:00
|
|
|
self.signatures = keypairs
|
|
|
|
.iter()
|
2019-01-25 23:41:20 -07:00
|
|
|
.map(|keypair| keypair.sign_message(&message))
|
2018-11-16 08:04:46 -08:00
|
|
|
.collect();
|
|
|
|
}
|
|
|
|
|
2019-03-07 07:37:22 -07:00
|
|
|
/// Check keys and keypair lengths, then sign this transaction.
|
|
|
|
/// Note: this presumes signatures.capacity() was set to the number of required signatures.
|
2019-03-14 15:55:28 -06:00
|
|
|
pub fn sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
|
2019-03-07 07:37:22 -07:00
|
|
|
let signed_keys = &self.account_keys[0..self.signatures.capacity()];
|
|
|
|
for (i, keypair) in keypairs.iter().enumerate() {
|
|
|
|
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
|
|
|
|
}
|
|
|
|
assert_eq!(keypairs.len(), signed_keys.len(), "not enough keypairs");
|
|
|
|
|
2019-03-14 15:55:28 -06:00
|
|
|
self.sign_unchecked(keypairs, recent_blockhash);
|
2019-03-07 07:37:22 -07:00
|
|
|
}
|
|
|
|
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Verify only the transaction signature.
|
|
|
|
pub fn verify_signature(&self) -> bool {
|
|
|
|
self.signatures
|
|
|
|
.iter()
|
2019-01-25 23:41:20 -07:00
|
|
|
.all(|s| s.verify(&self.from().as_ref(), &self.message()))
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Verify that references in the instructions are valid
|
|
|
|
pub fn verify_refs(&self) -> bool {
|
|
|
|
for instruction in &self.instructions {
|
|
|
|
if (instruction.program_ids_index as usize) >= self.program_ids.len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for account_index in &instruction.accounts {
|
|
|
|
if (*account_index as usize) >= self.account_keys.len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from(&self) -> &Pubkey {
|
|
|
|
&self.account_keys[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
// a hash of a slice of transactions only needs to hash the signatures
|
|
|
|
pub fn hash(transactions: &[Transaction]) -> Hash {
|
|
|
|
let mut hasher = Hasher::default();
|
2019-03-02 14:01:53 -08:00
|
|
|
transactions.iter().for_each(|tx| {
|
|
|
|
if !tx.signatures.is_empty() {
|
|
|
|
hasher.hash(&tx.signatures[0].as_ref());
|
|
|
|
}
|
|
|
|
});
|
2018-11-16 08:04:46 -08:00
|
|
|
hasher.result()
|
|
|
|
}
|
2019-01-24 21:14:15 -08:00
|
|
|
|
|
|
|
pub fn serialized_size(&self) -> Result<u64, Error> {
|
|
|
|
let mut buf = [0u8; size_of::<u64>() + 1];
|
|
|
|
let mut wr = Cursor::new(&mut buf[..]);
|
|
|
|
let mut size = size_of::<u64>();
|
|
|
|
|
|
|
|
let len = self.signatures.len();
|
|
|
|
encode_len(&mut wr, len)?;
|
|
|
|
size += wr.position() as usize + (len * size_of::<Signature>());
|
|
|
|
|
|
|
|
let len = self.account_keys.len();
|
|
|
|
wr.set_position(0);
|
|
|
|
encode_len(&mut wr, len)?;
|
|
|
|
size += wr.position() as usize + (len * size_of::<Pubkey>());
|
|
|
|
|
|
|
|
size += size_of::<Hash>();
|
|
|
|
|
|
|
|
size += size_of::<u64>();
|
|
|
|
|
|
|
|
let len = self.program_ids.len();
|
|
|
|
wr.set_position(0);
|
|
|
|
encode_len(&mut wr, len)?;
|
|
|
|
size += wr.position() as usize + (len * size_of::<Pubkey>());
|
|
|
|
|
|
|
|
let len = self.instructions.len();
|
|
|
|
wr.set_position(0);
|
|
|
|
encode_len(&mut wr, len)?;
|
|
|
|
size += wr.position() as usize;
|
|
|
|
let inst_size: u64 = self
|
|
|
|
.instructions
|
|
|
|
.iter()
|
|
|
|
.map(|ix| ix.serialized_size().unwrap())
|
|
|
|
.sum();
|
|
|
|
Ok(size as u64 + inst_size)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_signature(writer: &mut Cursor<&mut [u8]>, sig: &Signature) -> Result<(), Error> {
|
|
|
|
writer.write_all(sig.as_ref())?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_pubkey(writer: &mut Cursor<&mut [u8]>, key: &Pubkey) -> Result<(), Error> {
|
|
|
|
writer.write_all(key.as_ref())?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deserialize_signature(reader: &mut Cursor<&[u8]>) -> Result<Signature, Error> {
|
|
|
|
let mut buf = [0; size_of::<Signature>()];
|
|
|
|
reader.read_exact(&mut buf)?;
|
|
|
|
Ok(Signature::new(&buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deserialize_pubkey(reader: &mut Cursor<&[u8]>) -> Result<Pubkey, Error> {
|
|
|
|
let mut buf = [0; size_of::<Pubkey>()];
|
|
|
|
reader.read_exact(&mut buf)?;
|
|
|
|
Ok(Pubkey::new(&buf))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for Transaction {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
use serde::ser::Error;
|
|
|
|
let mut buf = vec![0u8; self.serialized_size().unwrap() as usize];
|
|
|
|
let mut wr = Cursor::new(&mut buf[..]);
|
|
|
|
serialize_vec_with(&mut wr, &self.signatures, Transaction::serialize_signature)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
serialize_vec_with(&mut wr, &self.account_keys, Transaction::serialize_pubkey)
|
|
|
|
.map_err(Error::custom)?;
|
2019-03-02 10:25:16 -08:00
|
|
|
wr.write_all(self.recent_blockhash.as_ref())
|
2019-03-02 10:20:10 -08:00
|
|
|
.map_err(Error::custom)?;
|
2019-01-24 21:14:15 -08:00
|
|
|
wr.write_u64::<LittleEndian>(self.fee)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
serialize_vec_with(&mut wr, &self.program_ids, Transaction::serialize_pubkey)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
serialize_vec_with(&mut wr, &self.instructions, Instruction::serialize_with)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
let size = wr.position() as usize;
|
|
|
|
serializer.serialize_bytes(&wr.into_inner()[..size])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TransactionVisitor;
|
|
|
|
impl<'a> serde::de::Visitor<'a> for TransactionVisitor {
|
|
|
|
type Value = Transaction;
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
formatter.write_str("Expecting Instruction")
|
|
|
|
}
|
|
|
|
fn visit_bytes<E>(self, data: &[u8]) -> Result<Transaction, E>
|
|
|
|
where
|
|
|
|
E: serde::de::Error,
|
|
|
|
{
|
|
|
|
use serde::de::Error;
|
|
|
|
let mut rd = Cursor::new(&data[..]);
|
|
|
|
let signatures: Vec<Signature> =
|
|
|
|
deserialize_vec_with(&mut rd, Transaction::deserialize_signature)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
let account_keys: Vec<Pubkey> =
|
|
|
|
deserialize_vec_with(&mut rd, Transaction::deserialize_pubkey)
|
|
|
|
.map_err(Error::custom)?;
|
|
|
|
let mut buf = [0; size_of::<Hash>()];
|
|
|
|
rd.read_exact(&mut buf).map_err(Error::custom)?;
|
2019-03-02 10:25:16 -08:00
|
|
|
let recent_blockhash: Hash = Hash::new(&buf);
|
2019-01-24 21:14:15 -08:00
|
|
|
let fee = rd.read_u64::<LittleEndian>().map_err(Error::custom)?;
|
|
|
|
let program_ids: Vec<Pubkey> =
|
|
|
|
deserialize_vec_with(&mut rd, Transaction::deserialize_pubkey)
|
|
|
|
.map_err(Error::custom)?;
|
2019-02-28 02:19:04 -07:00
|
|
|
let instructions: Vec<Instruction<u8, u8>> =
|
2019-01-24 21:14:15 -08:00
|
|
|
deserialize_vec_with(&mut rd, Instruction::deserialize_from).map_err(Error::custom)?;
|
|
|
|
Ok(Transaction {
|
|
|
|
signatures,
|
|
|
|
account_keys,
|
2019-03-02 10:25:16 -08:00
|
|
|
recent_blockhash,
|
2019-01-24 21:14:15 -08:00
|
|
|
fee,
|
|
|
|
program_ids,
|
|
|
|
instructions,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Transaction {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: ::serde::Deserializer<'de>,
|
|
|
|
{
|
|
|
|
deserializer.deserialize_bytes(TransactionVisitor)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-03-14 14:15:00 -06:00
|
|
|
use crate::signature::Keypair;
|
2019-01-24 21:14:15 -08:00
|
|
|
use bincode::deserialize;
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_refs() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
|
|
|
let prog1 = Keypair::new().pubkey();
|
|
|
|
let prog2 = Keypair::new().pubkey();
|
|
|
|
let instructions = vec![
|
|
|
|
Instruction::new(0, &(), vec![0, 1]),
|
|
|
|
Instruction::new(1, &(), vec![0, 2]),
|
|
|
|
];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&key],
|
|
|
|
&[key1, key2],
|
2019-02-09 09:20:43 -08:00
|
|
|
Hash::default(),
|
2018-11-16 08:04:46 -08:00
|
|
|
0,
|
|
|
|
vec![prog1, prog2],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
assert!(tx.verify_refs());
|
|
|
|
|
|
|
|
assert_eq!(tx.key(0, 0), Some(&key.pubkey()));
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(0, 0), Some(&key.pubkey()));
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(tx.key(1, 0), Some(&key.pubkey()));
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(1, 0), Some(&key.pubkey()));
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(tx.key(0, 1), Some(&key1));
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(0, 1), None);
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(tx.key(1, 1), Some(&key2));
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(1, 1), None);
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(tx.key(2, 0), None);
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(2, 0), None);
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(tx.key(0, 2), None);
|
2018-11-29 12:32:16 -08:00
|
|
|
assert_eq!(tx.signer_key(0, 2), None);
|
2018-11-16 08:04:46 -08:00
|
|
|
|
|
|
|
assert_eq!(*tx.program_id(0), prog1);
|
|
|
|
assert_eq!(*tx.program_id(1), prog2);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_refs_invalid_program_id() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let instructions = vec![Instruction::new(1, &(), vec![])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&key],
|
|
|
|
&[],
|
2019-02-09 09:20:43 -08:00
|
|
|
Hash::default(),
|
2018-11-16 08:04:46 -08:00
|
|
|
0,
|
|
|
|
vec![],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
assert!(!tx.verify_refs());
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_refs_invalid_account() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![1])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&key],
|
|
|
|
&[],
|
2019-02-09 09:20:43 -08:00
|
|
|
Hash::default(),
|
2018-11-16 08:04:46 -08:00
|
|
|
0,
|
2019-02-09 09:20:43 -08:00
|
|
|
vec![Pubkey::default()],
|
2018-11-16 08:04:46 -08:00
|
|
|
instructions,
|
|
|
|
);
|
2019-02-09 09:20:43 -08:00
|
|
|
assert_eq!(*tx.program_id(0), Pubkey::default());
|
2018-11-16 08:04:46 -08:00
|
|
|
assert!(!tx.verify_refs());
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:14:15 -08:00
|
|
|
#[test]
|
|
|
|
fn test_transaction_serialize() {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let program_id = Pubkey::new(&[4; 32]);
|
|
|
|
let to = Pubkey::new(&[5; 32]);
|
|
|
|
let tx = Transaction::new(
|
|
|
|
&keypair,
|
|
|
|
&[keypair.pubkey(), to],
|
2019-03-09 19:28:43 -08:00
|
|
|
&program_id,
|
2019-01-24 21:14:15 -08:00
|
|
|
&(1u8, 2u8, 3u8),
|
|
|
|
Hash::default(),
|
|
|
|
99,
|
|
|
|
);
|
|
|
|
|
|
|
|
let ser = serialize(&tx).unwrap();
|
|
|
|
let deser = deserialize(&ser).unwrap();
|
|
|
|
assert_eq!(tx, deser);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_transaction_serialized_size() {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let program_id = Pubkey::new(&[4; 32]);
|
|
|
|
let to = Pubkey::new(&[5; 32]);
|
|
|
|
let tx = Transaction::new(
|
|
|
|
&keypair,
|
|
|
|
&[keypair.pubkey(), to],
|
2019-03-09 19:28:43 -08:00
|
|
|
&program_id,
|
2019-01-24 21:14:15 -08:00
|
|
|
&(1u8, 2u8, 3u8),
|
|
|
|
Hash::default(),
|
|
|
|
99,
|
|
|
|
);
|
|
|
|
let req_size = size_of::<u64>()
|
|
|
|
+ 1
|
|
|
|
+ (tx.signatures.len() * size_of::<Signature>())
|
|
|
|
+ 1
|
|
|
|
+ (tx.account_keys.len() * size_of::<Pubkey>())
|
|
|
|
+ size_of::<Hash>()
|
|
|
|
+ size_of::<u64>()
|
|
|
|
+ 1
|
|
|
|
+ (tx.program_ids.len() * size_of::<Pubkey>())
|
|
|
|
+ 1
|
|
|
|
+ tx.instructions[0].serialized_size().unwrap() as usize;
|
|
|
|
let size = tx.serialized_size().unwrap() as usize;
|
|
|
|
assert_eq!(req_size, size);
|
|
|
|
}
|
|
|
|
|
2019-03-14 10:48:27 -06:00
|
|
|
/// Detect binary changes in the serialized transaction data, which could have a downstream
|
2018-11-16 08:04:46 -08:00
|
|
|
/// affect on SDKs and DApps
|
|
|
|
#[test]
|
|
|
|
fn test_sdk_serialize() {
|
|
|
|
use untrusted::Input;
|
|
|
|
let keypair = Keypair::from_pkcs8(Input::from(&[
|
|
|
|
48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 255, 101, 36, 24, 124, 23,
|
|
|
|
167, 21, 132, 204, 155, 5, 185, 58, 121, 75, 156, 227, 116, 193, 215, 38, 142, 22, 8,
|
|
|
|
14, 229, 239, 119, 93, 5, 218, 161, 35, 3, 33, 0, 36, 100, 158, 252, 33, 161, 97, 185,
|
|
|
|
62, 89, 99, 195, 250, 249, 187, 189, 171, 118, 241, 90, 248, 14, 68, 219, 231, 62, 157,
|
|
|
|
5, 142, 27, 210, 117,
|
2018-12-07 20:01:28 -07:00
|
|
|
]))
|
|
|
|
.unwrap();
|
2018-11-16 08:04:46 -08:00
|
|
|
let to = Pubkey::new(&[
|
|
|
|
1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4,
|
|
|
|
1, 1, 1,
|
|
|
|
]);
|
|
|
|
|
|
|
|
let program_id = Pubkey::new(&[
|
|
|
|
2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4,
|
|
|
|
2, 2, 2,
|
|
|
|
]);
|
|
|
|
|
|
|
|
let tx = Transaction::new(
|
|
|
|
&keypair,
|
2019-03-14 14:09:31 -06:00
|
|
|
&[to],
|
2019-03-09 19:28:43 -08:00
|
|
|
&program_id,
|
2018-11-16 08:04:46 -08:00
|
|
|
&(1u8, 2u8, 3u8),
|
|
|
|
Hash::default(),
|
|
|
|
99,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serialize(&tx).unwrap(),
|
|
|
|
vec![
|
2019-03-14 14:09:31 -06:00
|
|
|
212, 0, 0, 0, 0, 0, 0, 0, 1, 107, 231, 179, 42, 11, 220, 153, 173, 229, 29, 51,
|
|
|
|
218, 98, 26, 46, 164, 248, 228, 118, 244, 191, 192, 198, 228, 190, 119, 21, 52, 66,
|
|
|
|
25, 124, 247, 192, 73, 48, 231, 2, 70, 34, 82, 133, 137, 148, 66, 73, 231, 72, 195,
|
|
|
|
100, 133, 214, 2, 168, 108, 252, 200, 83, 99, 105, 51, 216, 145, 30, 14, 2, 36,
|
|
|
|
100, 158, 252, 33, 161, 97, 185, 62, 89, 99, 195, 250, 249, 187, 189, 171, 118,
|
|
|
|
241, 90, 248, 14, 68, 219, 231, 62, 157, 5, 142, 27, 210, 117, 1, 1, 1, 4, 5, 6, 7,
|
|
|
|
8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 99, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, 1, 0, 2, 0, 1, 3, 1, 2, 3
|
|
|
|
]
|
2018-11-16 08:04:46 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|