From 99d242804147ac8e3e510bc5d74655f5fac4f99b Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 22 May 2019 18:23:16 -0400 Subject: [PATCH] Transaction format changes toward Credit-Only accounts (#4386) * Add num_readonly_accounts slice * Impl programs in account_keys * Emulate current account-loading functionality using program-account_keys (breaks exchange_program_api tests) * Fix test * Add temporary exchange faucet id * Update chacha golden * Split num_credit_only_accounts into separate fields * Improve readability * Move message field constants into Message * Add MessageHeader struct and fixup comments --- core/src/chacha.rs | 2 +- core/src/sigverify.rs | 16 ++-- drone/src/drone.rs | 5 +- programs/budget_api/src/budget_processor.rs | 6 +- .../exchange_api/src/exchange_processor.rs | 24 ++++-- programs/exchange_api/src/lib.rs | 19 +++++ runtime/src/accounts.rs | 38 ++++++--- runtime/src/message_processor.rs | 9 +- sdk/src/fee_calculator.rs | 2 +- sdk/src/instruction.rs | 2 +- sdk/src/message.rs | 82 +++++++++++-------- sdk/src/transaction.rs | 42 +++++----- 12 files changed, 156 insertions(+), 91 deletions(-) diff --git a/core/src/chacha.rs b/core/src/chacha.rs index c8c4f8fa4..b0ce6f762 100644 --- a/core/src/chacha.rs +++ b/core/src/chacha.rs @@ -154,7 +154,7 @@ mod tests { hasher.hash(&buf[..size]); // golden needs to be updated if blob stuff changes.... - let golden: Hash = "5Pz5KQyNht2nqkJhVd8F9zTFxzoDvbQSzaxQbtCPiyCo" + let golden: Hash = "9xb2Asf7UK5G8WqPwsvzo5xwLi4dixBSDiYKCtYRikA" .parse() .unwrap(); diff --git a/core/src/sigverify.rs b/core/src/sigverify.rs index 616dd4b72..fc2fd2dbb 100644 --- a/core/src/sigverify.rs +++ b/core/src/sigverify.rs @@ -6,7 +6,9 @@ use crate::packet::{Packet, Packets}; use crate::result::Result; +use bincode::serialized_size; use solana_metrics::inc_new_counter_debug; +use solana_sdk::message::MessageHeader; use solana_sdk::pubkey::Pubkey; use solana_sdk::short_vec::decode_len; use solana_sdk::signature::Signature; @@ -16,9 +18,6 @@ use std::mem::size_of; type TxOffsets = (Vec, Vec, Vec, Vec, Vec>); -// The serialized size of Message::num_required_signatures. -const NUM_REQUIRED_SIGNATURES_SIZE: usize = 1; - #[cfg(feature = "cuda")] #[repr(C)] struct Elems { @@ -116,7 +115,8 @@ 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 + NUM_REQUIRED_SIGNATURES_SIZE + pubkey_size; + let pubkey_start = + msg_start + serialized_size(&MessageHeader::default()).unwrap() as usize + pubkey_size; ( sig_len as u32, @@ -389,19 +389,19 @@ mod tests { #[test] fn test_get_packet_offsets() { - 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)); + assert_eq!(get_packet_offsets_from_tx(test_tx(), 0), (1, 1, 64, 4)); + assert_eq!(get_packet_offsets_from_tx(test_tx(), 100), (1, 1, 64, 4)); // 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, 2) + (1, 1, 64, 4) ); // Ensure we're returning sig_len, not sig_size. assert_eq!( get_packet_offsets_from_tx(test_multisig_tx(), 0), - (2, 1, 128, 2) + (2, 1, 128, 4) ); } diff --git a/drone/src/drone.rs b/drone/src/drone.rs index c5ea5d4c0..17048d36f 100644 --- a/drone/src/drone.rs +++ b/drone/src/drone.rs @@ -382,7 +382,10 @@ mod tests { let message = tx.message(); assert_eq!(tx.signatures.len(), 1); - assert_eq!(message.account_keys, vec![mint_pubkey, to]); + assert_eq!( + message.account_keys, + vec![mint_pubkey, to, Pubkey::default()] + ); assert_eq!(message.recent_blockhash, blockhash); assert_eq!(message.instructions.len(), 1); diff --git a/programs/budget_api/src/budget_processor.rs b/programs/budget_api/src/budget_processor.rs index fa172d00e..dddbce843 100644 --- a/programs/budget_api/src/budget_processor.rs +++ b/programs/budget_api/src/budget_processor.rs @@ -209,8 +209,9 @@ mod tests { let mut message = Message::new(vec![instruction]); // Attack! Part 2: Point the instruction to the expected, but unsigned, key. - message.account_keys.push(alice_pubkey); + message.account_keys.insert(3, alice_pubkey); message.instructions[0].accounts[0] = 3; + message.instructions[0].program_ids_index = 4; // Ensure the transaction fails because of the unsigned key. assert_eq!( @@ -257,8 +258,9 @@ mod tests { let mut message = Message::new(vec![instruction]); // Attack! Part 2: Point the instruction to the expected, but unsigned, key. - message.account_keys.push(alice_pubkey); + message.account_keys.insert(3, alice_pubkey); message.instructions[0].accounts[0] = 3; + message.instructions[0].program_ids_index = 4; // Ensure the transaction fails because of the unsigned key. assert_eq!( diff --git a/programs/exchange_api/src/exchange_processor.rs b/programs/exchange_api/src/exchange_processor.rs index c19fd2229..58d7871bd 100644 --- a/programs/exchange_api/src/exchange_processor.rs +++ b/programs/exchange_api/src/exchange_processor.rs @@ -2,7 +2,7 @@ use crate::exchange_instruction::*; use crate::exchange_state::*; -use crate::id; +use crate::faucet_id; use log::*; use solana_metrics::inc_new_counter_info; use solana_sdk::account::KeyedAccount; @@ -192,7 +192,7 @@ impl ExchangeProcessor { let mut to_account = Self::deserialize_account(&keyed_accounts[TO_ACCOUNT_INDEX].account.data)?; - if &id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() { + if &faucet_id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() { to_account.tokens[token] += tokens; } else { let state: ExchangeState = @@ -460,7 +460,7 @@ pub fn process_instruction( #[cfg(test)] mod test { use super::*; - use crate::exchange_instruction; + use crate::{exchange_instruction, id}; use solana_runtime::bank::Bank; use solana_runtime::bank_client::BankClient; use solana_sdk::client::SyncClient; @@ -588,8 +588,13 @@ mod test { } fn transfer(client: &BankClient, owner: &Keypair, to: &Pubkey, token: Token, tokens: u64) { - let instruction = - exchange_instruction::transfer_request(&owner.pubkey(), to, &id(), token, tokens); + let instruction = exchange_instruction::transfer_request( + &owner.pubkey(), + to, + &faucet_id(), + token, + tokens, + ); client .send_instruction(owner, instruction) .expect(&format!("{}:{}", line!(), file!())); @@ -664,8 +669,13 @@ mod test { let new = create_token_account(&client, &owner); - let instruction = - exchange_instruction::transfer_request(&owner.pubkey(), &new, &id(), Token::A, 42); + let instruction = exchange_instruction::transfer_request( + &owner.pubkey(), + &new, + &faucet_id(), + Token::A, + 42, + ); client .send_instruction(&owner, instruction) .expect(&format!("{}:{}", line!(), file!())); diff --git a/programs/exchange_api/src/lib.rs b/programs/exchange_api/src/lib.rs index 87b358297..f41174ca2 100644 --- a/programs/exchange_api/src/lib.rs +++ b/programs/exchange_api/src/lib.rs @@ -11,3 +11,22 @@ pub const EXCHANGE_PROGRAM_ID: [u8; 32] = [ ]; solana_sdk::solana_program_id!(EXCHANGE_PROGRAM_ID); + +pub const EXCHANGE_FAUCET_ID: [u8; 32] = [ + 3, 147, 111, 103, 210, 47, 23, 11, 176, 29, 147, 89, 237, 155, 21, 62, 107, 105, 157, 1, 98, + 204, 206, 211, 54, 212, 79, 15, 160, 0, 0, 0, +]; + +pub fn faucet_id() -> solana_sdk::pubkey::Pubkey { + solana_sdk::pubkey::Pubkey::new(&EXCHANGE_FAUCET_ID) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn exchange_faucet_id() { + let ids = [("ExchangeFaucet11111111111111111111111111111", faucet_id())]; + assert!(ids.iter().all(|(name, id)| *name == id.to_string())); + } +} diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index c4c8b654b..fc5977634 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -180,11 +180,13 @@ impl Accounts { // If a fee can pay for execution then the program will be scheduled let mut called_accounts: Vec = vec![]; for key in &message.account_keys { - called_accounts.push( - AccountsDB::load(storage, ancestors, accounts_index, key) - .map(|(account, _)| account) - .unwrap_or_default(), - ); + if !message.program_ids().contains(&key) { + called_accounts.push( + AccountsDB::load(storage, ancestors, accounts_index, key) + .map(|(account, _)| account) + .unwrap_or_default(), + ); + } } if called_accounts.is_empty() || called_accounts[0].lamports == 0 { error_counters.account_not_found += 1; @@ -255,11 +257,11 @@ impl Accounts { .instructions .iter() .map(|ix| { - if message.program_ids().len() <= ix.program_ids_index as usize { + if message.account_keys.len() <= ix.program_ids_index as usize { error_counters.account_not_found += 1; return Err(TransactionError::AccountNotFound); } - let program_id = message.program_ids()[ix.program_ids_index as usize]; + let program_id = message.account_keys[ix.program_ids_index as usize]; Self::load_executable_accounts( storage, ancestors, @@ -469,9 +471,11 @@ impl Accounts { let rv = txs .iter() .map(|tx| { + let message = tx.borrow().message(); Self::lock_account( (&mut self.account_locks.lock().unwrap(), parent_record_locks), - &tx.borrow().message().account_keys, + &message.account_keys[..(message.account_keys.len() + - message.header.num_credit_only_unsigned_accounts as usize)], &mut error_counters, ) }) @@ -493,7 +497,12 @@ impl Accounts { { let record_locks = self.record_locks.lock().unwrap(); for tx in txs { - Self::lock_record_account(&record_locks.0, &tx.borrow().message().account_keys); + let message = tx.borrow().message(); + Self::lock_record_account( + &record_locks.0, + &message.account_keys[..(message.account_keys.len() + - message.header.num_credit_only_unsigned_accounts as usize)], + ); } } @@ -684,7 +693,10 @@ mod tests { assert_eq!(error_counters.account_not_found, 1); assert_eq!(loaded_accounts.len(), 1); - assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound)); + assert_eq!( + loaded_accounts[0], + Err(TransactionError::ProgramAccountNotFound) + ); } #[test] @@ -736,7 +748,7 @@ mod tests { let account = Account::new(2, 1, &Pubkey::default()); accounts.push((key1, account)); - let instructions = vec![CompiledInstruction::new(0, &(), vec![0, 1])]; + let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])]; let tx = Transaction::new_with_compiled_instructions( &[&keypair], &[key1], @@ -807,7 +819,7 @@ mod tests { account.owner = key5; accounts.push((key6, account)); - let instructions = vec![CompiledInstruction::new(0, &(), vec![0])]; + let instructions = vec![CompiledInstruction::new(1, &(), vec![0])]; let tx = Transaction::new_with_compiled_instructions( &[&keypair], &[], @@ -918,8 +930,8 @@ mod tests { accounts.push((key3, account)); let instructions = vec![ - CompiledInstruction::new(0, &(), vec![0]), CompiledInstruction::new(1, &(), vec![0]), + CompiledInstruction::new(2, &(), vec![0]), ]; let tx = Transaction::new_with_compiled_instructions( &[&keypair], diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 2c84292d3..06ba6e34a 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -125,7 +125,7 @@ impl MessageProcessor { program_accounts: &mut [&mut Account], tick_height: u64, ) -> Result<(), InstructionError> { - let program_id = instruction.program_id(message.program_ids()); + let program_id = instruction.program_id(&message.account_keys); let mut keyed_accounts = create_keyed_accounts(executable_accounts); let mut keyed_accounts2: Vec<_> = instruction @@ -134,7 +134,7 @@ impl MessageProcessor { .map(|&index| { let index = index as usize; let key = &message.account_keys[index]; - (key, index < message.num_required_signatures as usize) + (key, index < message.header.num_required_signatures as usize) }) .zip(program_accounts.iter_mut()) .map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account)) @@ -173,7 +173,7 @@ impl MessageProcessor { program_accounts: &mut [&mut Account], tick_height: u64, ) -> Result<(), InstructionError> { - let program_id = instruction.program_id(message.program_ids()); + let program_id = instruction.program_id(&message.account_keys); // 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(); @@ -221,7 +221,8 @@ impl MessageProcessor { tick_height: u64, ) -> Result<(), TransactionError> { for (instruction_index, instruction) in message.instructions.iter().enumerate() { - let executable_accounts = &mut loaders[instruction.program_ids_index as usize]; + let executable_accounts = &mut loaders + [message.program_index_in_program_ids(instruction.program_ids_index) as usize]; let mut program_accounts = get_subset_unchecked_mut(accounts, &instruction.accounts) .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?; self.execute_instruction( diff --git a/sdk/src/fee_calculator.rs b/sdk/src/fee_calculator.rs index c23307cbf..7335c5fc4 100644 --- a/sdk/src/fee_calculator.rs +++ b/sdk/src/fee_calculator.rs @@ -14,7 +14,7 @@ impl FeeCalculator { } pub fn calculate_fee(&self, message: &Message) -> u64 { - self.lamports_per_signature * u64::from(message.num_required_signatures) + self.lamports_per_signature * u64::from(message.header.num_required_signatures) } } diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index db29d227c..62b0ccefe 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -107,7 +107,7 @@ impl AccountMeta { /// An instruction to execute a program #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CompiledInstruction { - /// Index into the transaction program ids array indicating the program account that executes this instruction + /// Index into the transaction keys array indicating the program account that executes this instruction pub program_ids_index: u8, /// Ordered indices into the transaction keys array indicating which accounts to pass to the program #[serde(with = "short_vec")] diff --git a/sdk/src/message.rs b/sdk/src/message.rs index 0a48d5225..1ed33aa9d 100644 --- a/sdk/src/message.rs +++ b/sdk/src/message.rs @@ -10,11 +10,7 @@ fn position(keys: &[Pubkey], key: &Pubkey) -> u8 { keys.iter().position(|k| k == key).unwrap() as u8 } -fn compile_instruction( - ix: Instruction, - keys: &[Pubkey], - program_ids: &[Pubkey], -) -> CompiledInstruction { +fn compile_instruction(ix: Instruction, keys: &[Pubkey]) -> CompiledInstruction { let accounts: Vec<_> = ix .accounts .iter() @@ -22,19 +18,15 @@ fn compile_instruction( .collect(); CompiledInstruction { - program_ids_index: position(program_ids, &ix.program_ids_index), + program_ids_index: position(keys, &ix.program_ids_index), data: ix.data.clone(), accounts, } } -fn compile_instructions( - ixs: Vec, - keys: &[Pubkey], - program_ids: &[Pubkey], -) -> Vec { +fn compile_instructions(ixs: Vec, keys: &[Pubkey]) -> Vec { ixs.into_iter() - .map(|ix| compile_instruction(ix, keys, program_ids)) + .map(|ix| compile_instruction(ix, keys)) .collect() } @@ -78,12 +70,27 @@ fn get_program_ids(instructions: &[Instruction]) -> Vec { .collect() } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub struct Message { +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] +pub struct MessageHeader { /// The number of signatures required for this message to be considered valid. The /// signatures must match the first `num_required_signatures` of `account_keys`. pub num_required_signatures: u8, + /// The last num_credit_only_signed_accounts of the signed keys are credit-only accounts. + /// Programs may process multiple transactions that add lamports to the same credit-only + /// account within a single PoH entry, but are not permitted to debit lamports or modify + /// account data. Transactions targeting the same debit account are evaluated sequentially. + pub num_credit_only_signed_accounts: u8, + + /// The last num_credit_only_unsigned_accounts of the unsigned keys are credit-only accounts. + pub num_credit_only_unsigned_accounts: u8, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Message { + /// The message header, identifying signed and credit-only `account_keys` + pub header: MessageHeader, + /// All the account keys used by this transaction #[serde(with = "short_vec")] pub account_keys: Vec, @@ -91,10 +98,6 @@ pub struct Message { /// The id of a recent ledger entry. pub recent_blockhash: Hash, - /// All the program id keys used to execute this transaction's instructions - #[serde(with = "short_vec")] - program_ids: Vec, - /// Programs that will be executed in sequence and committed in one atomic transaction if all /// succeed. #[serde(with = "short_vec")] @@ -104,16 +107,20 @@ pub struct Message { impl Message { pub fn new_with_compiled_instructions( num_required_signatures: u8, + num_credit_only_signed_accounts: u8, + num_credit_only_unsigned_accounts: u8, account_keys: Vec, recent_blockhash: Hash, - program_ids: Vec, instructions: Vec, ) -> Self { Self { - num_required_signatures, + header: MessageHeader { + num_required_signatures, + num_credit_only_signed_accounts, + num_credit_only_unsigned_accounts, + }, account_keys, recent_blockhash, - program_ids, instructions, } } @@ -126,19 +133,28 @@ impl Message { let program_ids = get_program_ids(&instructions); let (mut signed_keys, unsigned_keys) = get_keys(&instructions, payer); let num_required_signatures = signed_keys.len() as u8; + let num_credit_only_signed_accounts = 0; + let num_credit_only_unsigned_accounts = program_ids.len() as u8; signed_keys.extend(&unsigned_keys); - let instructions = compile_instructions(instructions, &signed_keys, &program_ids); + signed_keys.extend(&program_ids); + let instructions = compile_instructions(instructions, &signed_keys); Self::new_with_compiled_instructions( num_required_signatures, + num_credit_only_signed_accounts, + num_credit_only_unsigned_accounts, signed_keys, Hash::default(), - program_ids, instructions, ) } pub fn program_ids(&self) -> &[Pubkey] { - &self.program_ids + &self.account_keys + [self.account_keys.len() - self.header.num_credit_only_unsigned_accounts as usize..] + } + + pub fn program_index_in_program_ids(&self, index: u8) -> u8 { + index - (self.account_keys.len() as u8 - self.header.num_credit_only_unsigned_accounts) } } @@ -293,16 +309,16 @@ mod tests { let id0 = Pubkey::default(); let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]); let message = Message::new(vec![ix]); - assert_eq!(message.num_required_signatures, 0); + assert_eq!(message.header.num_required_signatures, 0); let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]); let message = Message::new(vec![ix]); - assert_eq!(message.num_required_signatures, 1); + assert_eq!(message.header.num_required_signatures, 1); } #[test] fn test_message_kitchen_sink() { - let program_id0 = Pubkey::default(); + let program_id0 = Pubkey::new_rand(); let program_id1 = Pubkey::new_rand(); let id0 = Pubkey::default(); let keypair1 = Keypair::new(); @@ -314,15 +330,15 @@ mod tests { ]); assert_eq!( message.instructions[0], - CompiledInstruction::new(0, &0, vec![1]) + CompiledInstruction::new(2, &0, vec![1]) ); assert_eq!( message.instructions[1], - CompiledInstruction::new(1, &0, vec![0]) + CompiledInstruction::new(3, &0, vec![0]) ); assert_eq!( message.instructions[2], - CompiledInstruction::new(0, &0, vec![0]) + CompiledInstruction::new(2, &0, vec![0]) ); } @@ -334,11 +350,11 @@ mod tests { let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]); let message = Message::new_with_payer(vec![ix], Some(&payer)); - assert_eq!(message.num_required_signatures, 1); + assert_eq!(message.header.num_required_signatures, 1); let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]); let message = Message::new_with_payer(vec![ix], Some(&payer)); - assert_eq!(message.num_required_signatures, 2); + assert_eq!(message.header.num_required_signatures, 2); let ix = Instruction::new( program_id, @@ -346,7 +362,7 @@ mod tests { vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)], ); let message = Message::new_with_payer(vec![ix], Some(&payer)); - assert_eq!(message.num_required_signatures, 2); + assert_eq!(message.header.num_required_signatures, 2); } } diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index 3d256f5ff..c5f5f0f7d 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -67,7 +67,7 @@ pub struct Transaction { impl Transaction { pub fn new_unsigned(message: Message) -> Self { Self { - signatures: vec![Signature::default(); message.num_required_signatures as usize], + signatures: vec![Signature::default(); message.header.num_required_signatures as usize], message, } } @@ -115,11 +115,13 @@ impl Transaction { .map(|keypair| (*keypair).pubkey()) .collect(); account_keys.extend_from_slice(keys); + account_keys.extend(&program_ids); let message = Message::new_with_compiled_instructions( from_keypairs.len() as u8, + 0, + program_ids.len() as u8, account_keys, Hash::default(), - program_ids, instructions, ); Transaction::new(from_keypairs, message, recent_blockhash) @@ -175,7 +177,7 @@ impl Transaction { /// Check keys and keypair lengths, then sign this transaction. pub fn sign(&mut self, keypairs: &[&T], recent_blockhash: Hash) { let signed_keys = - &self.message.account_keys[0..self.message.num_required_signatures as usize]; + &self.message.account_keys[0..self.message.header.num_required_signatures as usize]; for (i, keypair) in keypairs.iter().enumerate() { assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch"); } @@ -188,7 +190,7 @@ impl Transaction { /// clear any prior signatures and update recent_blockhash pub fn partial_sign(&mut self, keypairs: &[&T], recent_blockhash: Hash) { let signed_keys = - &self.message.account_keys[0..self.message.num_required_signatures as usize]; + &self.message.account_keys[0..self.message.header.num_required_signatures as usize]; // if you change the blockhash, you're re-signing... if recent_blockhash != self.message.recent_blockhash { @@ -218,7 +220,7 @@ impl Transaction { pub fn verify_refs(&self) -> bool { let message = self.message(); for instruction in &message.instructions { - if (instruction.program_ids_index as usize) >= message.program_ids().len() { + if (instruction.program_ids_index as usize) >= message.account_keys.len() { return false; } for account_index in &instruction.accounts { @@ -304,7 +306,7 @@ mod tests { #[test] fn test_refs_invalid_account() { let key = Keypair::new(); - let instructions = vec![CompiledInstruction::new(0, &(), vec![1])]; + let instructions = vec![CompiledInstruction::new(0, &(), vec![2])]; let tx = Transaction::new_with_compiled_instructions( &[&key], &[], @@ -380,18 +382,18 @@ mod tests { let len_size = 1; let num_required_sigs_size = 1; + let num_credit_only_accounts_size = 2; let blockhash_size = size_of::(); let expected_transaction_size = len_size + (tx.signatures.len() * size_of::()) + num_required_sigs_size + + num_credit_only_accounts_size + len_size + (tx.message.account_keys.len() * size_of::()) + blockhash_size + len_size - + (tx.message.program_ids().len() * size_of::()) - + len_size + expected_instruction_size; - assert_eq!(expected_transaction_size, 214); + assert_eq!(expected_transaction_size, 215); assert_eq!( serialized_size(&tx).unwrap() as usize, @@ -407,16 +409,16 @@ mod tests { assert_eq!( serialize(&create_sample_transaction()).unwrap(), vec![ - 1, 134, 84, 186, 62, 126, 175, 48, 6, 80, 185, 139, 108, 109, 157, 213, 17, 249, 3, - 79, 83, 21, 89, 242, 148, 51, 140, 115, 77, 161, 134, 116, 136, 206, 171, 239, 236, - 240, 19, 73, 217, 152, 60, 159, 170, 41, 104, 29, 217, 93, 65, 139, 191, 202, 181, - 77, 246, 26, 15, 156, 186, 66, 32, 139, 6, 1, 2, 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, 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, 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, 71, 59, 9, 187, 190, 129, 150, 165, 21, 33, 158, 72, 87, 110, 144, 120, 79, 238, + 132, 134, 105, 39, 102, 116, 209, 29, 229, 154, 36, 105, 44, 172, 118, 131, 22, + 124, 131, 179, 142, 176, 27, 117, 160, 89, 102, 224, 204, 1, 252, 141, 2, 136, 0, + 37, 218, 225, 129, 92, 154, 250, 59, 97, 178, 10, 1, 0, 1, 3, 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, 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, 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, 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, 1, 2, + 2, 0, 1, 3, 1, 2, 3 ] ); } @@ -498,7 +500,7 @@ mod tests { tx.sign(&[&keypair0], Hash::default()); assert_eq!( tx.message.instructions[0], - CompiledInstruction::new(0, &0, vec![0]) + CompiledInstruction::new(1, &0, vec![0]) ); assert!(tx.is_signed()); }