Integrate Message into Transaction
This commit is contained in:
parent
98d60e6124
commit
31f8b6d352
|
@ -508,7 +508,7 @@ fn do_tx_transfers(
|
|||
}
|
||||
|
||||
fn verify_funding_transfer(client: &ThinClient, tx: &Transaction, amount: u64) -> bool {
|
||||
for a in &tx.account_keys[1..] {
|
||||
for a in &tx.message().account_keys[1..] {
|
||||
if client.get_balance(a).unwrap_or(0) >= amount {
|
||||
return true;
|
||||
}
|
||||
|
@ -577,7 +577,7 @@ fn fund_keys(client: &ThinClient, source: &Keypair, dests: &[Keypair], lamports:
|
|||
while !to_fund_txs.is_empty() {
|
||||
let receivers = to_fund_txs
|
||||
.iter()
|
||||
.fold(0, |len, (_, tx)| len + tx.instructions.len());
|
||||
.fold(0, |len, (_, tx)| len + tx.message().instructions.len());
|
||||
|
||||
println!(
|
||||
"{} {} to {} in {} txs",
|
||||
|
|
|
@ -196,7 +196,7 @@ impl RpcClient {
|
|||
|
||||
// Re-sign any failed transactions with a new blockhash and retry
|
||||
let blockhash =
|
||||
self.get_new_blockhash(&transactions_signatures[0].0.recent_blockhash)?;
|
||||
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
|
||||
transactions = transactions_signatures
|
||||
.into_iter()
|
||||
.map(|(mut transaction, _)| {
|
||||
|
@ -212,7 +212,7 @@ impl RpcClient {
|
|||
tx: &mut Transaction,
|
||||
signer_key: &T,
|
||||
) -> Result<(), Box<dyn error::Error>> {
|
||||
let blockhash = self.get_new_blockhash(&tx.recent_blockhash)?;
|
||||
let blockhash = self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||
tx.sign(&[signer_key], blockhash);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -744,10 +744,10 @@ mod tests {
|
|||
|
||||
assert_ne!(prev_tx, tx);
|
||||
assert_ne!(prev_tx.signatures, tx.signatures);
|
||||
assert_ne!(prev_tx.recent_blockhash, tx.recent_blockhash);
|
||||
assert_eq!(prev_tx.fee, tx.fee);
|
||||
assert_eq!(prev_tx.account_keys, tx.account_keys);
|
||||
assert_eq!(prev_tx.instructions, tx.instructions);
|
||||
assert_ne!(
|
||||
prev_tx.message().recent_blockhash,
|
||||
tx.message().recent_blockhash
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,8 +67,8 @@ fn bench_banking_stage_multi_accounts(bencher: &mut Bencher) {
|
|||
let from: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
let to: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
new.account_keys[0] = Pubkey::new(&from[0..32]);
|
||||
new.account_keys[1] = Pubkey::new(&to[0..32]);
|
||||
new.message.account_keys[0] = Pubkey::new(&from[0..32]);
|
||||
new.message.account_keys[1] = Pubkey::new(&to[0..32]);
|
||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||
new
|
||||
})
|
||||
|
@ -77,7 +77,7 @@ fn bench_banking_stage_multi_accounts(bencher: &mut Bencher) {
|
|||
transactions.iter().for_each(|tx| {
|
||||
let fund = SystemTransaction::new_move(
|
||||
&mint_keypair,
|
||||
&tx.account_keys[0],
|
||||
&tx.message.account_keys[0],
|
||||
mint_total / txes as u64,
|
||||
genesis_block.hash(),
|
||||
0,
|
||||
|
@ -159,25 +159,25 @@ fn bench_banking_stage_multi_programs(bencher: &mut Bencher) {
|
|||
let from: Vec<u8> = (0..32).map(|_| thread_rng().gen()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
let to: Vec<u8> = (0..32).map(|_| thread_rng().gen()).collect();
|
||||
new.account_keys[0] = Pubkey::new(&from[0..32]);
|
||||
new.account_keys[1] = Pubkey::new(&to[0..32]);
|
||||
let prog = new.instructions[0].clone();
|
||||
new.message.account_keys[0] = Pubkey::new(&from[0..32]);
|
||||
new.message.account_keys[1] = Pubkey::new(&to[0..32]);
|
||||
let prog = new.message.instructions[0].clone();
|
||||
for i in 1..progs {
|
||||
//generate programs that spend to random keys
|
||||
let to: Vec<u8> = (0..32).map(|_| thread_rng().gen()).collect();
|
||||
let to_key = Pubkey::new(&to[0..32]);
|
||||
new.account_keys.push(to_key);
|
||||
assert_eq!(new.account_keys.len(), i + 2);
|
||||
new.instructions.push(prog.clone());
|
||||
assert_eq!(new.instructions.len(), i + 1);
|
||||
new.instructions[i].accounts[1] = 1 + i as u8;
|
||||
new.message.account_keys.push(to_key);
|
||||
assert_eq!(new.message.account_keys.len(), i + 2);
|
||||
new.message.instructions.push(prog.clone());
|
||||
assert_eq!(new.message.instructions.len(), i + 1);
|
||||
new.message.instructions[i].accounts[1] = 1 + i as u8;
|
||||
assert_eq!(new.key(i, 1), Some(&to_key));
|
||||
assert_eq!(
|
||||
new.account_keys[new.instructions[i].accounts[1] as usize],
|
||||
new.message.account_keys[new.message.instructions[i].accounts[1] as usize],
|
||||
to_key
|
||||
);
|
||||
}
|
||||
assert_eq!(new.instructions.len(), progs);
|
||||
assert_eq!(new.message.instructions.len(), progs);
|
||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||
new
|
||||
})
|
||||
|
@ -185,7 +185,7 @@ fn bench_banking_stage_multi_programs(bencher: &mut Bencher) {
|
|||
transactions.iter().for_each(|tx| {
|
||||
let fund = SystemTransaction::new_move(
|
||||
&mint_keypair,
|
||||
&tx.account_keys[0],
|
||||
&tx.message.account_keys[0],
|
||||
mint_total / txes as u64,
|
||||
genesis_block.hash(),
|
||||
0,
|
||||
|
|
|
@ -164,7 +164,7 @@ mod tests {
|
|||
use bs58;
|
||||
// golden needs to be updated if blob stuff changes....
|
||||
let golden = Hash::new(
|
||||
&bs58::decode("7CESTE6TiU1kz3HXoDA6fYUhfnneqbSm75zLYCBdczzp")
|
||||
&bs58::decode("GnNzHbBRnn1WbrAXudmyxcqhaFiXQdzYWZpi6ToMM4pW")
|
||||
.into_vec()
|
||||
.unwrap(),
|
||||
);
|
||||
|
|
|
@ -128,7 +128,7 @@ pub fn get_packet_offsets(packet: &Packet, current_offset: u32) -> (u32, u32, u3
|
|||
|
||||
let sig_start = current_offset as usize + sig_size;
|
||||
let msg_start = current_offset as usize + msg_start_offset;
|
||||
let pubkey_start = msg_start + pubkey_size;
|
||||
let pubkey_start = msg_start + 1 + pubkey_size;
|
||||
|
||||
(
|
||||
sig_len as u32,
|
||||
|
@ -367,7 +367,7 @@ mod tests {
|
|||
Some(SIG_OFFSET)
|
||||
);
|
||||
assert_eq!(
|
||||
memfind(&tx_bytes, &tx.account_keys[0].as_ref()),
|
||||
memfind(&tx_bytes, &tx.message().account_keys[0].as_ref()),
|
||||
Some(pubkey_offset as usize)
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -385,7 +385,7 @@ mod tests {
|
|||
fn test_system_transaction_data_layout() {
|
||||
use crate::packet::PACKET_DATA_SIZE;
|
||||
let mut tx0 = test_tx();
|
||||
tx0.instructions[0].data = vec![1, 2, 3];
|
||||
tx0.message.instructions[0].data = vec![1, 2, 3];
|
||||
let message0a = tx0.message_data();
|
||||
let tx_bytes = serialize(&tx0).unwrap();
|
||||
assert!(tx_bytes.len() < PACKET_DATA_SIZE);
|
||||
|
@ -395,9 +395,9 @@ mod tests {
|
|||
);
|
||||
let tx1 = deserialize(&tx_bytes).unwrap();
|
||||
assert_eq!(tx0, tx1);
|
||||
assert_eq!(tx1.instructions[0].data, vec![1, 2, 3]);
|
||||
assert_eq!(tx1.message().instructions[0].data, vec![1, 2, 3]);
|
||||
|
||||
tx0.instructions[0].data = vec![1, 2, 4];
|
||||
tx0.message.instructions[0].data = vec![1, 2, 4];
|
||||
let message0b = tx0.message_data();
|
||||
assert_ne!(message0a, message0b);
|
||||
}
|
||||
|
@ -417,19 +417,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_get_packet_offsets() {
|
||||
assert_eq!(get_packet_offsets_from_tx(test_tx(), 0), (1, 1, 64, 1));
|
||||
assert_eq!(get_packet_offsets_from_tx(test_tx(), 100), (1, 1, 64, 1));
|
||||
assert_eq!(get_packet_offsets_from_tx(test_tx(), 0), (1, 1, 64, 2));
|
||||
assert_eq!(get_packet_offsets_from_tx(test_tx(), 100), (1, 1, 64, 2));
|
||||
|
||||
// Ensure we're not indexing packet by the `current_offset` parameter.
|
||||
assert_eq!(
|
||||
get_packet_offsets_from_tx(test_tx(), 1_000_000),
|
||||
(1, 1, 64, 1)
|
||||
(1, 1, 64, 2)
|
||||
);
|
||||
|
||||
// Ensure we're returning sig_len, not sig_size.
|
||||
assert_eq!(
|
||||
get_packet_offsets_from_tx(test_multisig_tx(), 0),
|
||||
(2, 1, 128, 1)
|
||||
(2, 1, 128, 2)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -401,9 +401,10 @@ impl StorageStage {
|
|||
// Go through the transactions, find proofs, and use them to update
|
||||
// the storage_keys with their signatures
|
||||
for tx in entry.transactions {
|
||||
for (i, program_id) in tx.program_ids.iter().enumerate() {
|
||||
let message = tx.message();
|
||||
for (i, program_id) in message.program_ids.iter().enumerate() {
|
||||
if solana_storage_api::check_id(&program_id) {
|
||||
match deserialize(&tx.instructions[i].data) {
|
||||
match deserialize(&message.instructions[i].data) {
|
||||
Ok(StorageInstruction::SubmitMiningProof {
|
||||
entry_height: proof_entry_height,
|
||||
signature,
|
||||
|
@ -436,7 +437,7 @@ impl StorageStage {
|
|||
(proof_entry_height / ENTRIES_PER_SEGMENT) as usize;
|
||||
if proof_segment_index < statew.replicator_map.len() {
|
||||
statew.replicator_map[proof_segment_index]
|
||||
.insert(tx.account_keys[0]);
|
||||
.insert(message.account_keys[0]);
|
||||
}
|
||||
}
|
||||
debug!("storage proof: entry_height: {}", entry_height);
|
||||
|
|
|
@ -352,13 +352,14 @@ mod tests {
|
|||
let mut drone = Drone::new(mint, None, None);
|
||||
|
||||
let tx = drone.build_airdrop_transaction(request).unwrap();
|
||||
let message = tx.message();
|
||||
|
||||
assert_eq!(tx.signatures.len(), 1);
|
||||
assert_eq!(tx.account_keys, vec![mint_pubkey, to]);
|
||||
assert_eq!(tx.recent_blockhash, blockhash);
|
||||
assert_eq!(message.account_keys, vec![mint_pubkey, to]);
|
||||
assert_eq!(message.recent_blockhash, blockhash);
|
||||
|
||||
assert_eq!(tx.instructions.len(), 1);
|
||||
let instruction: SystemInstruction = deserialize(&tx.instructions[0].data).unwrap();
|
||||
assert_eq!(message.instructions.len(), 1);
|
||||
let instruction: SystemInstruction = deserialize(&message.instructions[0].data).unwrap();
|
||||
assert_eq!(
|
||||
instruction,
|
||||
SystemInstruction::CreateAccount {
|
||||
|
|
|
@ -21,10 +21,12 @@ impl ExchangeTransaction {
|
|||
let space = mem::size_of::<ExchangeState>() as u64;
|
||||
let create_ix = SystemInstruction::new_program_account(owner_id, new, 1, space, &id());
|
||||
let request_ix = ExchangeInstruction::new_account_request(owner_id, new);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![create_ix, request_ix]);
|
||||
tx.fee = fee;
|
||||
tx.sign(&[owner], recent_blockhash);
|
||||
tx
|
||||
Transaction::new_signed_instructions(
|
||||
&[owner],
|
||||
vec![create_ix, request_ix],
|
||||
recent_blockhash,
|
||||
fee,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_transfer_request(
|
||||
|
@ -39,10 +41,7 @@ impl ExchangeTransaction {
|
|||
let owner_id = &owner.pubkey();
|
||||
let request_ix =
|
||||
ExchangeInstruction::new_transfer_request(owner_id, to, from, token, tokens);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![request_ix]);
|
||||
tx.fee = fee;
|
||||
tx.sign(&[owner], recent_blockhash);
|
||||
tx
|
||||
Transaction::new_signed_instructions(&[owner], vec![request_ix], recent_blockhash, fee)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -71,10 +70,12 @@ impl ExchangeTransaction {
|
|||
src_account,
|
||||
dst_account,
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![create_ix, request_ix]);
|
||||
tx.fee = fee;
|
||||
tx.sign(&[owner], recent_blockhash);
|
||||
tx
|
||||
Transaction::new_signed_instructions(
|
||||
&[owner],
|
||||
vec![create_ix, request_ix],
|
||||
recent_blockhash,
|
||||
fee,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_trade_cancellation(
|
||||
|
@ -86,10 +87,7 @@ impl ExchangeTransaction {
|
|||
) -> Transaction {
|
||||
let owner_id = &owner.pubkey();
|
||||
let request_ix = ExchangeInstruction::new_trade_cancellation(owner_id, trade, account);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![request_ix]);
|
||||
tx.fee = fee;
|
||||
tx.sign(&[owner], recent_blockhash);
|
||||
tx
|
||||
Transaction::new_signed_instructions(&[owner], vec![request_ix], recent_blockhash, fee)
|
||||
}
|
||||
|
||||
pub fn new_swap_request(
|
||||
|
@ -115,9 +113,11 @@ impl ExchangeTransaction {
|
|||
from_trade_account,
|
||||
profit_account,
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![create_ix, request_ix]);
|
||||
tx.fee = fee;
|
||||
tx.sign(&[owner], recent_blockhash);
|
||||
tx
|
||||
Transaction::new_signed_instructions(
|
||||
&[owner],
|
||||
vec![create_ix, request_ix],
|
||||
recent_blockhash,
|
||||
fee,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -599,9 +599,9 @@ impl AccountsDB {
|
|||
continue;
|
||||
}
|
||||
|
||||
let tx = &txs[i];
|
||||
let message = &txs[i].message();
|
||||
let acc = raccs.as_ref().unwrap();
|
||||
for (key, account) in tx.account_keys.iter().zip(acc.0.iter()) {
|
||||
for (key, account) in message.account_keys.iter().zip(acc.0.iter()) {
|
||||
self.store(fork, key, account);
|
||||
}
|
||||
}
|
||||
|
@ -614,11 +614,12 @@ impl AccountsDB {
|
|||
error_counters: &mut ErrorCounters,
|
||||
) -> Result<Vec<Account>> {
|
||||
// Copy all the accounts
|
||||
if tx.signatures.is_empty() && tx.fee != 0 {
|
||||
let message = tx.message();
|
||||
if tx.signatures.is_empty() && message.fee != 0 {
|
||||
Err(TransactionError::MissingSignatureForFee)
|
||||
} else {
|
||||
// Check for unique account keys
|
||||
if has_duplicates(&tx.account_keys) {
|
||||
if has_duplicates(&message.account_keys) {
|
||||
error_counters.account_loaded_twice += 1;
|
||||
return Err(TransactionError::AccountLoadedTwice);
|
||||
}
|
||||
|
@ -626,17 +627,17 @@ impl AccountsDB {
|
|||
// There is no way to predict what program will execute without an error
|
||||
// If a fee can pay for execution then the program will be scheduled
|
||||
let mut called_accounts: Vec<Account> = vec![];
|
||||
for key in &tx.account_keys {
|
||||
for key in &message.account_keys {
|
||||
called_accounts.push(self.load(fork, key, true).unwrap_or_default());
|
||||
}
|
||||
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
|
||||
error_counters.account_not_found += 1;
|
||||
Err(TransactionError::AccountNotFound)
|
||||
} else if called_accounts[0].lamports < tx.fee {
|
||||
} else if called_accounts[0].lamports < message.fee {
|
||||
error_counters.insufficient_funds += 1;
|
||||
Err(TransactionError::InsufficientFundsForFee)
|
||||
} else {
|
||||
called_accounts[0].lamports -= tx.fee;
|
||||
called_accounts[0].lamports -= message.fee;
|
||||
Ok(called_accounts)
|
||||
}
|
||||
}
|
||||
|
@ -690,14 +691,16 @@ impl AccountsDB {
|
|||
tx: &Transaction,
|
||||
error_counters: &mut ErrorCounters,
|
||||
) -> Result<Vec<Vec<(Pubkey, Account)>>> {
|
||||
tx.instructions
|
||||
let message = tx.message();
|
||||
message
|
||||
.instructions
|
||||
.iter()
|
||||
.map(|ix| {
|
||||
if tx.program_ids.len() <= ix.program_ids_index as usize {
|
||||
if message.program_ids.len() <= ix.program_ids_index as usize {
|
||||
error_counters.account_not_found += 1;
|
||||
return Err(TransactionError::AccountNotFound);
|
||||
}
|
||||
let program_id = tx.program_ids[ix.program_ids_index as usize];
|
||||
let program_id = message.program_ids[ix.program_ids_index as usize];
|
||||
self.load_executable_accounts(fork, &program_id, error_counters)
|
||||
})
|
||||
.collect()
|
||||
|
@ -881,7 +884,7 @@ impl Accounts {
|
|||
Err(TransactionError::AccountInUse) => (),
|
||||
_ => {
|
||||
if let Some(locks) = account_locks.get_mut(&fork) {
|
||||
for k in &tx.account_keys {
|
||||
for k in &tx.message().account_keys {
|
||||
locks.remove(k);
|
||||
}
|
||||
if locks.is_empty() {
|
||||
|
@ -908,7 +911,7 @@ impl Accounts {
|
|||
Self::lock_account(
|
||||
fork,
|
||||
&mut account_locks,
|
||||
&tx.account_keys,
|
||||
&tx.message().account_keys,
|
||||
&mut error_counters,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -497,7 +497,9 @@ impl Bank {
|
|||
txs.iter()
|
||||
.zip(lock_results.into_iter())
|
||||
.map(|(tx, lock_res)| {
|
||||
if lock_res.is_ok() && !hash_queue.check_hash_age(tx.recent_blockhash, max_age) {
|
||||
if lock_res.is_ok()
|
||||
&& !hash_queue.check_hash_age(tx.message().recent_blockhash, max_age)
|
||||
{
|
||||
error_counters.reserve_blockhash += 1;
|
||||
Err(TransactionError::BlockhashNotFound)
|
||||
} else {
|
||||
|
@ -566,7 +568,7 @@ impl Bank {
|
|||
Err(e) => Err(e.clone()),
|
||||
Ok((ref mut accounts, ref mut loaders)) => {
|
||||
self.runtime
|
||||
.execute_transaction(tx, loaders, accounts, tick_height)
|
||||
.process_message(tx.message(), loaders, accounts, tick_height)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
@ -652,18 +654,21 @@ impl Bank {
|
|||
let results = txs
|
||||
.iter()
|
||||
.zip(executed.iter())
|
||||
.map(|(tx, res)| match *res {
|
||||
Err(TransactionError::InstructionError(_, _)) => {
|
||||
// Charge the transaction fee even in case of InstructionError
|
||||
self.withdraw(&tx.account_keys[0], tx.fee)?;
|
||||
fees += tx.fee;
|
||||
Ok(())
|
||||
.map(|(tx, res)| {
|
||||
let message = tx.message();
|
||||
match *res {
|
||||
Err(TransactionError::InstructionError(_, _)) => {
|
||||
// Charge the transaction fee even in case of InstructionError
|
||||
self.withdraw(&message.account_keys[0], message.fee)?;
|
||||
fees += message.fee;
|
||||
Ok(())
|
||||
}
|
||||
Ok(()) => {
|
||||
fees += message.fee;
|
||||
Ok(())
|
||||
}
|
||||
_ => res.clone(),
|
||||
}
|
||||
Ok(()) => {
|
||||
fees += tx.fee;
|
||||
Ok(())
|
||||
}
|
||||
_ => res.clone(),
|
||||
})
|
||||
.collect();
|
||||
self.deposit(&self.collector_id, fees);
|
||||
|
@ -1308,14 +1313,14 @@ mod tests {
|
|||
);
|
||||
|
||||
let mut tx_invalid_program_index = tx.clone();
|
||||
tx_invalid_program_index.instructions[0].program_ids_index = 42;
|
||||
tx_invalid_program_index.message.instructions[0].program_ids_index = 42;
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx_invalid_program_index),
|
||||
Err(TransactionError::InvalidAccountIndex)
|
||||
);
|
||||
|
||||
let mut tx_invalid_account_index = tx.clone();
|
||||
tx_invalid_account_index.instructions[0].accounts[0] = 42;
|
||||
tx_invalid_account_index.message.instructions[0].accounts[0] = 42;
|
||||
assert_eq!(
|
||||
bank.process_transaction(&tx_invalid_account_index),
|
||||
Err(TransactionError::InvalidAccountIndex)
|
||||
|
@ -1598,7 +1603,7 @@ mod tests {
|
|||
|
||||
// Set the fee to 0, this should give an InstructionError
|
||||
// but since no signature we cannot look up the error.
|
||||
tx.fee = 0;
|
||||
tx.message.fee = 0;
|
||||
|
||||
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
||||
assert_eq!(bank.get_balance(&key.pubkey()), 0);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::native_loader;
|
||||
use solana_sdk::account::{create_keyed_accounts, Account, KeyedAccount};
|
||||
use solana_sdk::instruction::InstructionError;
|
||||
use solana_sdk::message::Message;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::system_program;
|
||||
use solana_sdk::transaction::{Transaction, TransactionError};
|
||||
use solana_sdk::transaction::TransactionError;
|
||||
|
||||
/// Return true if the slice has any duplicate elements
|
||||
pub fn has_duplicates<T: PartialEq>(xs: &[T]) -> bool {
|
||||
|
@ -113,22 +114,22 @@ impl Runtime {
|
|||
/// This method calls the instruction's program entrypoint method
|
||||
fn process_instruction(
|
||||
&self,
|
||||
tx: &Transaction,
|
||||
message: &Message,
|
||||
instruction_index: usize,
|
||||
executable_accounts: &mut [(Pubkey, Account)],
|
||||
program_accounts: &mut [&mut Account],
|
||||
tick_height: u64,
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = tx.program_id(instruction_index);
|
||||
let program_id = message.program_id(instruction_index);
|
||||
|
||||
let mut keyed_accounts = create_keyed_accounts(executable_accounts);
|
||||
let mut keyed_accounts2: Vec<_> = tx.instructions[instruction_index]
|
||||
let mut keyed_accounts2: Vec<_> = message.instructions[instruction_index]
|
||||
.accounts
|
||||
.iter()
|
||||
.map(|&index| {
|
||||
let index = index as usize;
|
||||
let key = &tx.account_keys[index];
|
||||
(key, index < tx.signatures.len())
|
||||
let key = &message.account_keys[index];
|
||||
(key, index < message.num_signatures as usize)
|
||||
})
|
||||
.zip(program_accounts.iter_mut())
|
||||
.map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account))
|
||||
|
@ -140,7 +141,7 @@ impl Runtime {
|
|||
return process_instruction(
|
||||
&program_id,
|
||||
&mut keyed_accounts[1..],
|
||||
&tx.instructions[instruction_index].data,
|
||||
&message.instructions[instruction_index].data,
|
||||
tick_height,
|
||||
);
|
||||
}
|
||||
|
@ -149,7 +150,7 @@ impl Runtime {
|
|||
native_loader::entrypoint(
|
||||
&program_id,
|
||||
&mut keyed_accounts,
|
||||
&tx.instructions[instruction_index].data,
|
||||
&message.instructions[instruction_index].data,
|
||||
tick_height,
|
||||
)
|
||||
}
|
||||
|
@ -160,13 +161,13 @@ impl Runtime {
|
|||
/// The accounts are committed back to the bank only if this function returns Ok(_).
|
||||
fn execute_instruction(
|
||||
&self,
|
||||
tx: &Transaction,
|
||||
message: &Message,
|
||||
instruction_index: usize,
|
||||
executable_accounts: &mut [(Pubkey, Account)],
|
||||
program_accounts: &mut [&mut Account],
|
||||
tick_height: u64,
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = tx.program_id(instruction_index);
|
||||
let program_id = message.program_id(instruction_index);
|
||||
// TODO: the runtime should be checking read/write access to memory
|
||||
// we are trusting the hard-coded programs not to clobber or allocate
|
||||
let pre_total: u64 = program_accounts.iter().map(|a| a.lamports).sum();
|
||||
|
@ -176,7 +177,7 @@ impl Runtime {
|
|||
.collect();
|
||||
|
||||
self.process_instruction(
|
||||
tx,
|
||||
message,
|
||||
instruction_index,
|
||||
executable_accounts,
|
||||
program_accounts,
|
||||
|
@ -204,22 +205,22 @@ impl Runtime {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute a transaction.
|
||||
/// This method calls each instruction in the transaction over the set of loaded Accounts
|
||||
/// Process a message.
|
||||
/// This method calls each instruction in the message over the set of loaded Accounts
|
||||
/// The accounts are committed back to the bank only if every instruction succeeds
|
||||
pub fn execute_transaction(
|
||||
pub fn process_message(
|
||||
&self,
|
||||
tx: &Transaction,
|
||||
message: &Message,
|
||||
loaders: &mut [Vec<(Pubkey, Account)>],
|
||||
tx_accounts: &mut [Account],
|
||||
accounts: &mut [Account],
|
||||
tick_height: u64,
|
||||
) -> Result<(), TransactionError> {
|
||||
for (instruction_index, instruction) in tx.instructions.iter().enumerate() {
|
||||
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||
let executable_accounts = &mut loaders[instruction.program_ids_index as usize];
|
||||
let mut program_accounts = get_subset_unchecked_mut(tx_accounts, &instruction.accounts)
|
||||
let mut program_accounts = get_subset_unchecked_mut(accounts, &instruction.accounts)
|
||||
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
||||
self.execute_instruction(
|
||||
tx,
|
||||
message,
|
||||
instruction_index,
|
||||
executable_accounts,
|
||||
&mut program_accounts,
|
||||
|
|
|
@ -70,19 +70,47 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> {
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Message {
|
||||
#[serde(skip)]
|
||||
pub num_signatures: u8,
|
||||
|
||||
/// All the account keys used by this transaction
|
||||
#[serde(with = "short_vec")]
|
||||
pub account_keys: Vec<Pubkey>,
|
||||
|
||||
/// The id of a recent ledger entry.
|
||||
pub recent_blockhash: Hash,
|
||||
|
||||
/// The number of lamports paid for processing and storing of this transaction.
|
||||
pub fee: u64,
|
||||
|
||||
/// All the program id keys used to execute this transaction's instructions
|
||||
#[serde(with = "short_vec")]
|
||||
pub program_ids: Vec<Pubkey>,
|
||||
|
||||
/// Programs that will be executed in sequence and committed in one atomic transaction if all
|
||||
/// succeed.
|
||||
#[serde(with = "short_vec")]
|
||||
pub instructions: Vec<CompiledInstruction>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn new_with_compiled_instructions(
|
||||
num_signatures: u8,
|
||||
account_keys: Vec<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
fee: u64,
|
||||
program_ids: Vec<Pubkey>,
|
||||
instructions: Vec<CompiledInstruction>,
|
||||
) -> Self {
|
||||
Self {
|
||||
num_signatures,
|
||||
account_keys,
|
||||
recent_blockhash,
|
||||
fee,
|
||||
program_ids,
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an unsigned transaction with space for requires signatures.
|
||||
pub fn new(instructions: Vec<Instruction>) -> Self {
|
||||
let program_ids = get_program_ids(&instructions);
|
||||
|
@ -90,14 +118,19 @@ impl Message {
|
|||
let num_signatures = signed_keys.len() as u8;
|
||||
signed_keys.extend(&unsigned_keys);
|
||||
let instructions = compile_instructions(instructions, &signed_keys, &program_ids);
|
||||
Self {
|
||||
Self::new_with_compiled_instructions(
|
||||
num_signatures,
|
||||
account_keys: signed_keys,
|
||||
recent_blockhash: Hash::default(),
|
||||
fee: 0,
|
||||
signed_keys,
|
||||
Hash::default(),
|
||||
0,
|
||||
program_ids,
|
||||
instructions,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,36 +53,16 @@ pub struct Transaction {
|
|||
/// signatures.len() keys of account_keys
|
||||
#[serde(with = "short_vec")]
|
||||
pub signatures: Vec<Signature>,
|
||||
/// All the account keys used by this transaction
|
||||
|
||||
#[serde(with = "short_vec")]
|
||||
pub account_keys: Vec<Pubkey>,
|
||||
|
||||
/// The id of a recent ledger entry.
|
||||
pub recent_blockhash: Hash,
|
||||
|
||||
/// The number of lamports paid for processing and storing of this transaction.
|
||||
pub fee: u64,
|
||||
|
||||
/// All the program id keys used to execute this transaction's instructions
|
||||
#[serde(with = "short_vec")]
|
||||
pub program_ids: Vec<Pubkey>,
|
||||
|
||||
/// Programs that will be executed in sequence and committed in one atomic transaction if all
|
||||
/// succeed.
|
||||
#[serde(with = "short_vec")]
|
||||
pub instructions: Vec<CompiledInstruction>,
|
||||
/// The message to sign.
|
||||
pub message: Message,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new_unsigned(message: Message) -> Self {
|
||||
Self {
|
||||
signatures: Vec::with_capacity(message.num_signatures as usize),
|
||||
account_keys: message.account_keys,
|
||||
recent_blockhash: message.recent_blockhash,
|
||||
fee: message.fee,
|
||||
program_ids: message.program_ids,
|
||||
instructions: message.instructions,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,31 +113,31 @@ impl Transaction {
|
|||
.map(|keypair| keypair.pubkey())
|
||||
.collect();
|
||||
account_keys.extend_from_slice(keys);
|
||||
let mut tx = Transaction {
|
||||
signatures: Vec::with_capacity(from_keypairs.len()),
|
||||
let message = Message::new_with_compiled_instructions(
|
||||
from_keypairs.len() as u8,
|
||||
account_keys,
|
||||
recent_blockhash: Hash::default(),
|
||||
Hash::default(),
|
||||
fee,
|
||||
program_ids,
|
||||
instructions,
|
||||
};
|
||||
tx.sign(from_keypairs, recent_blockhash);
|
||||
tx
|
||||
);
|
||||
Transaction::new(from_keypairs, message, recent_blockhash)
|
||||
}
|
||||
|
||||
pub fn data(&self, instruction_index: usize) -> &[u8] {
|
||||
&self.instructions[instruction_index].data
|
||||
&self.message.instructions[instruction_index].data
|
||||
}
|
||||
|
||||
fn key_index(&self, instruction_index: usize, accounts_index: usize) -> Option<usize> {
|
||||
self.instructions
|
||||
self.message
|
||||
.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))
|
||||
.and_then(|account_keys_index| self.message.account_keys.get(account_keys_index))
|
||||
}
|
||||
pub fn signer_key(&self, instruction_index: usize, accounts_index: usize) -> Option<&Pubkey> {
|
||||
match self.key_index(instruction_index, accounts_index) {
|
||||
|
@ -166,25 +146,17 @@ impl Transaction {
|
|||
if signature_index >= self.signatures.len() {
|
||||
return None;
|
||||
}
|
||||
self.account_keys.get(signature_index)
|
||||
self.message.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]
|
||||
self.message().program_id(instruction_index)
|
||||
}
|
||||
|
||||
/// Return a message containing all data that should be signed.
|
||||
pub fn message(&self) -> Message {
|
||||
Message {
|
||||
num_signatures: self.signatures.len() as u8,
|
||||
account_keys: self.account_keys.clone(),
|
||||
recent_blockhash: self.recent_blockhash,
|
||||
fee: self.fee,
|
||||
program_ids: self.program_ids.clone(),
|
||||
instructions: self.instructions.clone(),
|
||||
}
|
||||
pub fn message(&self) -> &Message {
|
||||
&self.message
|
||||
}
|
||||
|
||||
/// Return the serialized message data to sign.
|
||||
|
@ -194,7 +166,7 @@ impl Transaction {
|
|||
|
||||
/// Sign this transaction.
|
||||
pub fn sign_unchecked<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
|
||||
self.recent_blockhash = recent_blockhash;
|
||||
self.message.recent_blockhash = recent_blockhash;
|
||||
let message_data = self.message_data();
|
||||
self.signatures = keypairs
|
||||
.iter()
|
||||
|
@ -203,9 +175,8 @@ impl Transaction {
|
|||
}
|
||||
|
||||
/// Check keys and keypair lengths, then sign this transaction.
|
||||
/// Note: this presumes signatures.capacity() was set to the number of required signatures.
|
||||
pub fn sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
|
||||
let signed_keys = &self.account_keys[0..self.signatures.capacity()];
|
||||
let signed_keys = &self.message.account_keys[0..self.message.num_signatures as usize];
|
||||
for (i, keypair) in keypairs.iter().enumerate() {
|
||||
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
|
||||
}
|
||||
|
@ -216,12 +187,13 @@ impl Transaction {
|
|||
|
||||
/// 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() {
|
||||
let message = self.message();
|
||||
for instruction in &message.instructions {
|
||||
if (instruction.program_ids_index as usize) >= message.program_ids.len() {
|
||||
return false;
|
||||
}
|
||||
for account_index in &instruction.accounts {
|
||||
if (*account_index as usize) >= self.account_keys.len() {
|
||||
if (*account_index as usize) >= message.account_keys.len() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -379,14 +351,15 @@ mod tests {
|
|||
let expected_transaction_size = 1
|
||||
+ (tx.signatures.len() * size_of::<Signature>())
|
||||
+ 1
|
||||
+ (tx.account_keys.len() * size_of::<Pubkey>())
|
||||
+ 1
|
||||
+ (tx.message.account_keys.len() * size_of::<Pubkey>())
|
||||
+ size_of::<Hash>()
|
||||
+ size_of::<u64>()
|
||||
+ 1
|
||||
+ (tx.program_ids.len() * size_of::<Pubkey>())
|
||||
+ (tx.message.program_ids.len() * size_of::<Pubkey>())
|
||||
+ 1
|
||||
+ expected_instruction_size;
|
||||
assert_eq!(expected_transaction_size, 221);
|
||||
assert_eq!(expected_transaction_size, 222);
|
||||
|
||||
assert_eq!(
|
||||
serialized_size(&tx).unwrap() as usize,
|
||||
|
@ -402,16 +375,16 @@ mod tests {
|
|||
assert_eq!(
|
||||
serialize(&create_sample_transaction()).unwrap(),
|
||||
vec![
|
||||
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
|
||||
1, 102, 197, 120, 176, 192, 35, 190, 233, 5, 135, 30, 114, 209, 105, 219, 212, 203,
|
||||
242, 72, 150, 205, 68, 30, 188, 119, 31, 212, 40, 43, 88, 45, 239, 83, 45, 208,
|
||||
103, 108, 239, 82, 185, 160, 161, 111, 112, 51, 254, 61, 254, 131, 206, 221, 117,
|
||||
124, 50, 1, 6, 38, 218, 9, 95, 28, 46, 49, 5, 1, 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
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -452,6 +425,9 @@ mod tests {
|
|||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![ix]);
|
||||
tx.sign(&[&keypair0], Hash::default());
|
||||
assert_eq!(tx.instructions[0], CompiledInstruction::new(0, &0, vec![0]));
|
||||
assert_eq!(
|
||||
tx.message.instructions[0],
|
||||
CompiledInstruction::new(0, &0, vec![0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -693,7 +693,7 @@ impl KeypairUtil for DroneKeypair {
|
|||
|
||||
/// Return the public key of the keypair used to sign votes
|
||||
fn pubkey(&self) -> Pubkey {
|
||||
self.transaction.account_keys[0]
|
||||
self.transaction.message().account_keys[0]
|
||||
}
|
||||
|
||||
fn sign_message(&self, _msg: &[u8]) -> Signature {
|
||||
|
|
Loading…
Reference in New Issue