2018-09-26 10:07:53 -06:00
|
|
|
//! The `system_transaction` module provides functionality for creating system transactions.
|
|
|
|
|
2018-09-26 17:55:36 -06:00
|
|
|
use signature::{Keypair, KeypairUtil};
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2018-10-25 11:13:08 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::system_instruction::SystemInstruction;
|
2018-09-26 10:07:53 -06:00
|
|
|
use system_program::SystemProgram;
|
2018-11-16 08:04:46 -08:00
|
|
|
|
2018-10-05 16:45:27 -07:00
|
|
|
use transaction::{Instruction, Transaction};
|
2018-09-26 10:07:53 -06:00
|
|
|
|
|
|
|
pub trait SystemTransaction {
|
|
|
|
fn system_create(
|
|
|
|
from_keypair: &Keypair,
|
|
|
|
to: Pubkey,
|
|
|
|
last_id: Hash,
|
2018-11-05 09:36:22 -07:00
|
|
|
tokens: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
space: u64,
|
|
|
|
program_id: Pubkey,
|
2018-11-05 09:36:22 -07:00
|
|
|
fee: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
) -> Self;
|
|
|
|
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_assign(from_keypair: &Keypair, last_id: Hash, program_id: Pubkey, fee: u64) -> Self;
|
2018-09-26 10:07:53 -06:00
|
|
|
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self;
|
2018-09-26 10:07:53 -06:00
|
|
|
|
|
|
|
fn system_move(
|
|
|
|
from_keypair: &Keypair,
|
|
|
|
to: Pubkey,
|
2018-11-05 09:36:22 -07:00
|
|
|
tokens: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
last_id: Hash,
|
2018-11-05 09:36:22 -07:00
|
|
|
fee: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
) -> Self;
|
|
|
|
|
2018-10-05 16:45:27 -07:00
|
|
|
fn system_move_many(
|
|
|
|
from_keypair: &Keypair,
|
2018-11-05 09:36:22 -07:00
|
|
|
moves: &[(Pubkey, u64)],
|
2018-10-05 16:45:27 -07:00
|
|
|
last_id: Hash,
|
2018-11-05 09:36:22 -07:00
|
|
|
fee: u64,
|
2018-10-05 16:45:27 -07:00
|
|
|
) -> Self;
|
2018-10-18 10:33:30 -07:00
|
|
|
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self;
|
2018-09-26 10:07:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SystemTransaction for Transaction {
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::CreateAccount transaction
|
2018-09-26 10:07:53 -06:00
|
|
|
fn system_create(
|
|
|
|
from_keypair: &Keypair,
|
|
|
|
to: Pubkey,
|
|
|
|
last_id: Hash,
|
2018-11-05 09:36:22 -07:00
|
|
|
tokens: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
space: u64,
|
|
|
|
program_id: Pubkey,
|
2018-11-05 09:36:22 -07:00
|
|
|
fee: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
) -> Self {
|
2018-11-16 08:04:46 -08:00
|
|
|
let create = SystemInstruction::CreateAccount {
|
2018-09-26 10:07:53 -06:00
|
|
|
tokens, //TODO, the tokens to allocate might need to be higher then 0 in the future
|
|
|
|
space,
|
|
|
|
program_id,
|
|
|
|
};
|
|
|
|
Transaction::new(
|
|
|
|
from_keypair,
|
|
|
|
&[to],
|
|
|
|
SystemProgram::id(),
|
2018-11-06 06:50:00 -07:00
|
|
|
&create,
|
2018-09-26 10:07:53 -06:00
|
|
|
last_id,
|
|
|
|
fee,
|
|
|
|
)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::Assign transaction
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_assign(from_keypair: &Keypair, last_id: Hash, program_id: Pubkey, fee: u64) -> Self {
|
2018-11-16 08:04:46 -08:00
|
|
|
let assign = SystemInstruction::Assign { program_id };
|
2018-09-26 10:07:53 -06:00
|
|
|
Transaction::new(
|
|
|
|
from_keypair,
|
|
|
|
&[],
|
|
|
|
SystemProgram::id(),
|
2018-11-06 06:50:00 -07:00
|
|
|
&assign,
|
2018-09-26 10:07:53 -06:00
|
|
|
last_id,
|
|
|
|
fee,
|
|
|
|
)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::CreateAccount transaction with some defaults
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self {
|
2018-09-26 10:07:53 -06:00
|
|
|
Transaction::system_create(from_keypair, to, last_id, tokens, 0, Pubkey::default(), 0)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::Move transaction
|
2018-09-26 10:07:53 -06:00
|
|
|
fn system_move(
|
|
|
|
from_keypair: &Keypair,
|
|
|
|
to: Pubkey,
|
2018-11-05 09:36:22 -07:00
|
|
|
tokens: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
last_id: Hash,
|
2018-11-05 09:36:22 -07:00
|
|
|
fee: u64,
|
2018-09-26 10:07:53 -06:00
|
|
|
) -> Self {
|
2018-11-16 08:04:46 -08:00
|
|
|
let move_tokens = SystemInstruction::Move { tokens };
|
2018-09-26 10:07:53 -06:00
|
|
|
Transaction::new(
|
|
|
|
from_keypair,
|
|
|
|
&[to],
|
|
|
|
SystemProgram::id(),
|
2018-11-06 06:50:00 -07:00
|
|
|
&move_tokens,
|
2018-09-26 10:07:53 -06:00
|
|
|
last_id,
|
|
|
|
fee,
|
|
|
|
)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::Move transaction to many destinations
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_move_many(from: &Keypair, moves: &[(Pubkey, u64)], last_id: Hash, fee: u64) -> Self {
|
2018-10-05 16:45:27 -07:00
|
|
|
let instructions: Vec<_> = moves
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, (_, amount))| {
|
2018-11-16 08:04:46 -08:00
|
|
|
let spend = SystemInstruction::Move { tokens: *amount };
|
2018-11-06 06:50:00 -07:00
|
|
|
Instruction::new(0, &spend, vec![0, i as u8 + 1])
|
2018-10-05 16:45:27 -07:00
|
|
|
}).collect();
|
|
|
|
let to_keys: Vec<_> = moves.iter().map(|(to_key, _)| *to_key).collect();
|
|
|
|
|
|
|
|
Transaction::new_with_instructions(
|
2018-10-26 14:43:34 -07:00
|
|
|
&[from],
|
2018-10-05 16:45:27 -07:00
|
|
|
&to_keys,
|
|
|
|
last_id,
|
|
|
|
fee,
|
|
|
|
vec![SystemProgram::id()],
|
|
|
|
instructions,
|
|
|
|
)
|
|
|
|
}
|
2018-11-16 08:04:46 -08:00
|
|
|
/// Create and sign new SystemInstruction::Spawn transaction
|
2018-11-05 09:36:22 -07:00
|
|
|
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self {
|
2018-11-16 08:04:46 -08:00
|
|
|
let spawn = SystemInstruction::Spawn;
|
2018-11-06 06:50:00 -07:00
|
|
|
Transaction::new(from_keypair, &[], SystemProgram::id(), &spawn, last_id, fee)
|
2018-10-18 10:33:30 -07:00
|
|
|
}
|
2018-09-26 10:07:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_tx() -> Transaction {
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let pubkey1 = keypair1.pubkey();
|
|
|
|
let zero = Hash::default();
|
|
|
|
Transaction::system_new(&keypair1, pubkey1, 42, zero)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub fn memfind<A: Eq>(a: &[A], b: &[A]) -> Option<usize> {
|
|
|
|
assert!(a.len() >= b.len());
|
|
|
|
let end = a.len() - b.len() + 1;
|
|
|
|
for i in 0..end {
|
|
|
|
if a[i..i + b.len()] == b[..] {
|
|
|
|
return Some(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use bincode::{deserialize, serialize};
|
|
|
|
use packet::PACKET_DATA_SIZE;
|
2018-10-26 14:43:34 -07:00
|
|
|
use sigverify;
|
|
|
|
use transaction::SIG_OFFSET;
|
2018-09-26 10:07:53 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_layout() {
|
|
|
|
let tx = test_tx();
|
|
|
|
let tx_bytes = serialize(&tx).unwrap();
|
2018-10-26 14:43:34 -07:00
|
|
|
let sign_data = tx.get_sign_data();
|
|
|
|
let packet = sigverify::make_packet_from_transaction(tx.clone());
|
|
|
|
|
|
|
|
let (sig_len, sig_start, msg_start_offset, pubkey_offset) =
|
|
|
|
sigverify::get_packet_offsets(&packet, 0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
memfind(&tx_bytes, &tx.signatures[0].as_ref()),
|
|
|
|
Some(SIG_OFFSET)
|
|
|
|
);
|
2018-09-26 10:07:53 -06:00
|
|
|
assert_eq!(
|
2018-09-28 16:16:35 -07:00
|
|
|
memfind(&tx_bytes, &tx.account_keys[0].as_ref()),
|
2018-10-26 14:43:34 -07:00
|
|
|
Some(pubkey_offset as usize)
|
2018-09-26 10:07:53 -06:00
|
|
|
);
|
2018-10-26 14:43:34 -07:00
|
|
|
assert_eq!(
|
|
|
|
memfind(&tx_bytes, &sign_data),
|
|
|
|
Some(msg_start_offset as usize)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
memfind(&tx_bytes, &tx.signatures[0].as_ref()),
|
|
|
|
Some(sig_start as usize)
|
|
|
|
);
|
|
|
|
assert_eq!(sig_len, 1);
|
2018-09-26 10:07:53 -06:00
|
|
|
assert!(tx.verify_signature());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_userdata_layout() {
|
|
|
|
let mut tx0 = test_tx();
|
2018-09-28 16:16:35 -07:00
|
|
|
tx0.instructions[0].userdata = vec![1, 2, 3];
|
2018-09-26 10:07:53 -06:00
|
|
|
let sign_data0a = tx0.get_sign_data();
|
|
|
|
let tx_bytes = serialize(&tx0).unwrap();
|
|
|
|
assert!(tx_bytes.len() < PACKET_DATA_SIZE);
|
|
|
|
assert_eq!(
|
2018-10-26 14:43:34 -07:00
|
|
|
memfind(&tx_bytes, &tx0.signatures[0].as_ref()),
|
2018-09-26 10:07:53 -06:00
|
|
|
Some(SIG_OFFSET)
|
|
|
|
);
|
|
|
|
let tx1 = deserialize(&tx_bytes).unwrap();
|
|
|
|
assert_eq!(tx0, tx1);
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(tx1.instructions[0].userdata, vec![1, 2, 3]);
|
2018-09-26 10:07:53 -06:00
|
|
|
|
2018-09-28 16:16:35 -07:00
|
|
|
tx0.instructions[0].userdata = vec![1, 2, 4];
|
2018-09-26 10:07:53 -06:00
|
|
|
let sign_data0b = tx0.get_sign_data();
|
|
|
|
assert_ne!(sign_data0a, sign_data0b);
|
|
|
|
}
|
2018-10-10 17:23:06 -07:00
|
|
|
#[test]
|
|
|
|
fn test_move_many() {
|
|
|
|
let from = Keypair::new();
|
|
|
|
let t1 = Keypair::new();
|
|
|
|
let t2 = Keypair::new();
|
|
|
|
let moves = vec![(t1.pubkey(), 1), (t2.pubkey(), 2)];
|
|
|
|
|
|
|
|
let tx = Transaction::system_move_many(&from, &moves, Default::default(), 0);
|
|
|
|
assert_eq!(tx.account_keys[0], from.pubkey());
|
|
|
|
assert_eq!(tx.account_keys[1], t1.pubkey());
|
|
|
|
assert_eq!(tx.account_keys[2], t2.pubkey());
|
|
|
|
assert_eq!(tx.instructions.len(), 2);
|
|
|
|
assert_eq!(tx.instructions[0].accounts, vec![0, 1]);
|
|
|
|
assert_eq!(tx.instructions[1].accounts, vec![0, 2]);
|
|
|
|
}
|
2018-09-26 10:07:53 -06:00
|
|
|
}
|