From 31f8b6d352bcc94c375af75ecdf2667e32104a83 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 29 Mar 2019 10:05:06 -0600 Subject: [PATCH] Integrate Message into Transaction --- bench-tps/src/bench.rs | 4 +- client/src/rpc_client.rs | 12 +- core/benches/banking_stage.rs | 28 ++--- core/src/chacha.rs | 2 +- core/src/sigverify.rs | 18 +-- core/src/storage_stage.rs | 7 +- drone/src/drone.rs | 9 +- .../exchange_api/src/exchange_transaction.rs | 40 +++---- runtime/src/accounts.rs | 27 +++-- runtime/src/bank.rs | 37 ++++--- runtime/src/runtime.rs | 39 +++---- sdk/src/message.rs | 45 +++++++- sdk/src/transaction.rs | 104 +++++++----------- wallet/src/wallet.rs | 2 +- 14 files changed, 197 insertions(+), 177 deletions(-) diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index 55c6c51ff..69cd00a3e 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -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", diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 50d8c8582..1d9b56636 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -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> { - 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 + ); } } diff --git a/core/benches/banking_stage.rs b/core/benches/banking_stage.rs index 817c675b8..feec976a3 100644 --- a/core/benches/banking_stage.rs +++ b/core/benches/banking_stage.rs @@ -67,8 +67,8 @@ fn bench_banking_stage_multi_accounts(bencher: &mut Bencher) { let from: Vec = (0..64).map(|_| thread_rng().gen()).collect(); let to: Vec = (0..64).map(|_| thread_rng().gen()).collect(); let sig: Vec = (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 = (0..32).map(|_| thread_rng().gen()).collect(); let sig: Vec = (0..64).map(|_| thread_rng().gen()).collect(); let to: Vec = (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 = (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, diff --git a/core/src/chacha.rs b/core/src/chacha.rs index 33b5f78e0..30876c6f0 100644 --- a/core/src/chacha.rs +++ b/core/src/chacha.rs @@ -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(), ); diff --git a/core/src/sigverify.rs b/core/src/sigverify.rs index ea6ddb41a..043e4d276 100644 --- a/core/src/sigverify.rs +++ b/core/src/sigverify.rs @@ -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) ); } diff --git a/core/src/storage_stage.rs b/core/src/storage_stage.rs index 791a44412..f26ae94a8 100644 --- a/core/src/storage_stage.rs +++ b/core/src/storage_stage.rs @@ -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); diff --git a/drone/src/drone.rs b/drone/src/drone.rs index 2a9312492..f91e73ca5 100644 --- a/drone/src/drone.rs +++ b/drone/src/drone.rs @@ -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 { diff --git a/programs/exchange_api/src/exchange_transaction.rs b/programs/exchange_api/src/exchange_transaction.rs index ea8420cda..3d65ad358 100644 --- a/programs/exchange_api/src/exchange_transaction.rs +++ b/programs/exchange_api/src/exchange_transaction.rs @@ -21,10 +21,12 @@ impl ExchangeTransaction { let space = mem::size_of::() 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, + ) } } diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 08bf91395..0a5d0a15d 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -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> { // 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 = 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>> { - 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, ) }) diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 8a1a3c9ac..694e39bde 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -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); diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index 0a013dc47..7e4355c65 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -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(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, diff --git a/sdk/src/message.rs b/sdk/src/message.rs index 27d581f26..333353513 100644 --- a/sdk/src/message.rs +++ b/sdk/src/message.rs @@ -70,19 +70,47 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec { #[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, + + /// 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, + + /// Programs that will be executed in sequence and committed in one atomic transaction if all + /// succeed. #[serde(with = "short_vec")] pub instructions: Vec, } impl Message { + pub fn new_with_compiled_instructions( + num_signatures: u8, + account_keys: Vec, + recent_blockhash: Hash, + fee: u64, + program_ids: Vec, + instructions: Vec, + ) -> 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) -> 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] } } diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index 3cada29fc..ceb9529f1 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -53,36 +53,16 @@ pub struct Transaction { /// signatures.len() keys of account_keys #[serde(with = "short_vec")] pub signatures: Vec, - /// All the account keys used by this transaction - #[serde(with = "short_vec")] - pub account_keys: Vec, - - /// 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, - - /// Programs that will be executed in sequence and committed in one atomic transaction if all - /// succeed. - #[serde(with = "short_vec")] - pub instructions: Vec, + /// 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 { - 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(&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(&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::()) + 1 - + (tx.account_keys.len() * size_of::()) + + 1 + + (tx.message.account_keys.len() * size_of::()) + size_of::() + size_of::() + 1 - + (tx.program_ids.len() * size_of::()) + + (tx.message.program_ids.len() * size_of::()) + 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]) + ); } } diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index 5298658a3..c2d6afe70 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -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 {