From 023074650f8e4ee6803dc84135b7f8cb4ad7bf9a Mon Sep 17 00:00:00 2001 From: Jack May Date: Wed, 22 Jan 2020 09:11:56 -0800 Subject: [PATCH] Allow the same account to be passed multiple times to a single instruction (#7795) --- programs/bpf/tests/programs.rs | 72 +++++ programs/bpf_loader/src/lib.rs | 164 ++++++---- programs/bpf_loader/test_elfs/noop.so | Bin 4384 -> 52344 bytes programs/budget/src/budget_processor.rs | 44 +-- programs/config/src/config_processor.rs | 59 ++-- programs/exchange/src/exchange_processor.rs | 56 ++-- programs/move_loader/src/processor.rs | 122 +++---- programs/ownable/src/ownable_processor.rs | 14 +- programs/stake/src/config.rs | 7 +- programs/stake/src/stake_instruction.rs | 79 +++-- programs/stake/src/stake_state.rs | 256 ++++++++------- programs/storage/src/storage_contract.rs | 12 +- programs/storage/src/storage_processor.rs | 31 +- programs/vest/src/vest_processor.rs | 33 +- programs/vote/src/vote_instruction.rs | 10 +- programs/vote/src/vote_state.rs | 68 ++-- runtime/benches/accounts.rs | 8 + runtime/benches/message_processor.rs | 8 - runtime/src/accounts.rs | 22 +- runtime/src/bank.rs | 123 ++++++- runtime/src/message_processor.rs | 336 +++++++++++++++----- runtime/src/native_loader.rs | 2 +- runtime/src/nonce_utils.rs | 12 +- runtime/src/storage_utils.rs | 93 +++--- runtime/src/system_instruction_processor.rs | 251 +++++++-------- sdk/src/account.rs | 101 +++++- sdk/src/account_utils.rs | 4 +- sdk/src/instruction.rs | 12 + sdk/src/instruction_processor_utils.rs | 4 +- sdk/src/nonce_state.rs | 121 +++---- sdk/src/sysvar/mod.rs | 3 +- sdk/src/sysvar/rent.rs | 5 +- sdk/src/transaction.rs | 25 ++ 33 files changed, 1392 insertions(+), 765 deletions(-) diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index b407ca1e32..7554e9830a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -77,6 +77,7 @@ mod bpf { #[cfg(feature = "bpf_rust")] mod bpf_rust { use super::*; + use solana_sdk::account::Account; use solana_sdk::bpf_loader; use solana_sdk::client::SyncClient; use solana_sdk::clock::DEFAULT_SLOTS_PER_EPOCH; @@ -142,5 +143,76 @@ mod bpf { } } } + + #[test] + fn test_program_bpf_rust_duplicate_accounts() { + solana_logger::setup(); + + let filename = create_bpf_path("solana_bpf_rust_dup_accounts"); + let mut file = File::open(filename).unwrap(); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + + let bank = Arc::new(Bank::new(&genesis_config)); + let bank_client = BankClient::new_shared(&bank); + let program_id = load_program(&bank_client, &mint_keypair, &bpf_loader::id(), elf); + + let payee_account = Account::new(10, 1, &program_id); + let payee_pubkey = Pubkey::new_rand(); + bank.store_account(&payee_pubkey, &payee_account); + + let account = Account::new(10, 1, &program_id); + let pubkey = Pubkey::new_rand(); + let account_metas = vec![ + AccountMeta::new(mint_keypair.pubkey(), true), + AccountMeta::new(payee_pubkey, false), + AccountMeta::new(pubkey, false), + AccountMeta::new(pubkey, false), + ]; + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &1u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); + assert!(result.is_ok()); + assert_eq!(data[0], 1); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &2u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + let data = bank_client.get_account_data(&pubkey).unwrap().unwrap(); + assert!(result.is_ok()); + assert_eq!(data[0], 2); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &3u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + assert!(!result.is_ok()); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &4u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + let lamports = bank_client.get_balance(&pubkey).unwrap(); + assert!(result.is_ok()); + assert_eq!(lamports, 11); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &5u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + let lamports = bank_client.get_balance(&pubkey).unwrap(); + assert!(result.is_ok()); + assert_eq!(lamports, 12); + + bank.store_account(&pubkey, &account); + let instruction = Instruction::new(program_id, &6u8, account_metas.clone()); + let result = bank_client.send_instruction(&mint_keypair, instruction); + assert!(!result.is_ok()); + } } } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index e45a9a0359..52a588265f 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -8,6 +8,7 @@ use log::*; use solana_rbpf::{memory_region::MemoryRegion, EbpfVm}; use solana_sdk::{ account::KeyedAccount, + hash::{Hash, Hasher}, instruction::InstructionError, instruction_processor_utils::{is_executable, limited_deserialize, next_keyed_account}, loader_instruction::LoaderInstruction, @@ -15,6 +16,7 @@ use solana_sdk::{ sysvar::rent, }; use std::{ + collections::HashMap, convert::TryFrom, io::{prelude::*, Error}, mem, @@ -48,7 +50,7 @@ pub fn serialize_parameters( program_id: &Pubkey, keyed_accounts: &mut [KeyedAccount], data: &[u8], -) -> Vec { +) -> Result, InstructionError> { assert_eq!(32, mem::size_of::()); let mut v: Vec = Vec::new(); @@ -58,39 +60,84 @@ pub fn serialize_parameters( v.write_u64::(keyed_account.signer_key().is_some() as u64) .unwrap(); v.write_all(keyed_account.unsigned_key().as_ref()).unwrap(); - v.write_u64::(keyed_account.account.lamports) + v.write_u64::(keyed_account.lamports()?) .unwrap(); - v.write_u64::(keyed_account.account.data.len() as u64) + v.write_u64::(keyed_account.data_len()? as u64) .unwrap(); - v.write_all(&keyed_account.account.data).unwrap(); - v.write_all(keyed_account.account.owner.as_ref()).unwrap(); + v.write_all(&keyed_account.try_account_ref()?.data).unwrap(); + v.write_all(keyed_account.owner()?.as_ref()).unwrap(); } v.write_u64::(data.len() as u64).unwrap(); v.write_all(data).unwrap(); v.write_all(program_id.as_ref()).unwrap(); - v + Ok(v) } -pub fn deserialize_parameters(keyed_accounts: &mut [KeyedAccount], buffer: &[u8]) { +pub fn deserialize_parameters( + keyed_accounts: &mut [KeyedAccount], + buffer: &[u8], +) -> Result<(), InstructionError> { assert_eq!(32, mem::size_of::()); + let calculate_hash = |lamports: u64, data: &[u8]| -> Hash { + let mut hasher = Hasher::default(); + let mut buf = [0u8; 8]; + LittleEndian::write_u64(&mut buf[..], lamports); + hasher.hash(&buf); + hasher.hash(data); + hasher.result() + }; + + // remember any duplicate accounts + let mut map: HashMap = HashMap::new(); + for (i, keyed_account) in keyed_accounts.iter().enumerate() { + if keyed_accounts[i + 1..].contains(keyed_account) + && !map.contains_key(keyed_account.unsigned_key()) + { + let hash = calculate_hash( + keyed_account.lamports()?, + &keyed_account.try_account_ref()?.data, + ); + map.insert(*keyed_account.unsigned_key(), (hash, false)); + } + } + let mut start = mem::size_of::(); for keyed_account in keyed_accounts.iter_mut() { - start += mem::size_of::(); // skip signer_key boolean - start += mem::size_of::(); // skip pubkey - keyed_account.account.lamports = LittleEndian::read_u64(&buffer[start..]); + start += mem::size_of::() // signer_key boolean + + mem::size_of::(); // pubkey + let lamports = LittleEndian::read_u64(&buffer[start..]); + start += mem::size_of::() // lamports + + mem::size_of::(); // length tag + let end = start + keyed_account.data_len()?; + let data_start = start; + let data_end = end; - start += mem::size_of::() // skip lamports - + mem::size_of::(); // skip length tag - let end = start + keyed_account.account.data.len(); - keyed_account - .account - .data - .clone_from_slice(&buffer[start..end]); + // if duplicate, modified, and dirty, then bail + let mut do_update = true; + if let Some((hash, is_dirty)) = map.get_mut(keyed_account.unsigned_key()) { + let new_hash = calculate_hash(lamports, &buffer[data_start..data_end]); + if *hash != new_hash { + if *is_dirty { + return Err(InstructionError::DuplicateAccountOutOfSync); + } + *is_dirty = true; // fail if modified again + } else { + do_update = false; // no changes, don't need to update account + } + } + if do_update { + keyed_account.try_account_ref_mut()?.lamports = lamports; + keyed_account + .try_account_ref_mut()? + .data + .clone_from_slice(&buffer[data_start..data_end]); + } - start += keyed_account.account.data.len() // skip data - + mem::size_of::(); // skip owner + start += keyed_account.data_len()? // data + + mem::size_of::(); // owner } + Ok(()) } pub fn process_instruction( @@ -105,15 +152,11 @@ pub fn process_instruction( return Err(InstructionError::NotEnoughAccountKeys); } - if is_executable(keyed_accounts) { + if is_executable(keyed_accounts)? { let mut keyed_accounts_iter = keyed_accounts.iter_mut(); let program = next_keyed_account(&mut keyed_accounts_iter)?; - - if !program.account.executable { - warn!("BPF program account not executable"); - return Err(InstructionError::AccountNotExecutable); - } - let (mut vm, heap_region) = match create_vm(&program.account.data) { + let program_account = program.try_account_ref_mut()?; + let (mut vm, heap_region) = match create_vm(&program_account.data) { Ok(info) => info, Err(e) => { warn!("Failed to create BPF VM: {}", e); @@ -122,7 +165,7 @@ pub fn process_instruction( }; let parameter_accounts = keyed_accounts_iter.into_slice(); let mut parameter_bytes = - serialize_parameters(program_id, parameter_accounts, &instruction_data); + serialize_parameters(program_id, parameter_accounts, &instruction_data)?; info!("Call BPF program"); match vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region]) { @@ -143,7 +186,7 @@ pub fn process_instruction( return Err(InstructionError::GenericError); } } - deserialize_parameters(parameter_accounts, ¶meter_bytes); + deserialize_parameters(parameter_accounts, ¶meter_bytes)?; info!("BPF program success"); } else if !keyed_accounts.is_empty() { match limited_deserialize(instruction_data)? { @@ -157,15 +200,11 @@ pub fn process_instruction( let offset = offset as usize; let len = bytes.len(); trace!("Write: offset={} length={}", offset, len); - if program.account.data.len() < offset + len { - warn!( - "Write overflow: {} < {}", - program.account.data.len(), - offset + len - ); + if program.data_len()? < offset + len { + warn!("Write overflow: {} < {}", program.data_len()?, offset + len); return Err(InstructionError::AccountDataTooSmall); } - program.account.data[offset..offset + len].copy_from_slice(&bytes); + program.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes); } LoaderInstruction::Finalize => { let mut keyed_accounts_iter = keyed_accounts.iter_mut(); @@ -177,14 +216,14 @@ pub fn process_instruction( return Err(InstructionError::MissingRequiredSignature); } - if let Err(e) = check_elf(&program.account.data) { + if let Err(e) = check_elf(&program.try_account_ref()?.data) { warn!("Invalid ELF: {}", e); return Err(InstructionError::InvalidAccountData); } rent::verify_rent_exemption(&program, &rent)?; - program.account.executable = true; + program.try_account_ref_mut()?.executable = true; info!("Finalize: account {:?}", program.signer_key().unwrap()); } } @@ -196,8 +235,7 @@ pub fn process_instruction( mod tests { use super::*; use solana_sdk::account::Account; - use std::fs::File; - use std::io::Read; + use std::{cell::RefCell, fs::File, io::Read}; #[test] #[should_panic(expected = "Error: Exceeded maximum number of instructions allowed")] @@ -221,7 +259,7 @@ mod tests { fn test_bpf_loader_write() { let program_id = Pubkey::new_rand(); let program_key = Pubkey::new_rand(); - let mut program_account = Account::new(1, 0, &program_id); + let mut program_account = Account::new_ref(1, 0, &program_id); let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)]; let instruction_data = bincode::serialize(&LoaderInstruction::Write { offset: 3, @@ -243,16 +281,19 @@ mod tests { // Case: Write bytes to an offset let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &mut program_account)]; - keyed_accounts[0].account.data = vec![0; 6]; + keyed_accounts[0].account.borrow_mut().data = vec![0; 6]; assert_eq!( Ok(()), process_instruction(&program_id, &mut keyed_accounts, &instruction_data) ); - assert_eq!(vec![0, 0, 0, 1, 2, 3], keyed_accounts[0].account.data); + assert_eq!( + vec![0, 0, 0, 1, 2, 3], + keyed_accounts[0].account.borrow().data + ); // Case: Overflow let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &mut program_account)]; - keyed_accounts[0].account.data = vec![0; 5]; + keyed_accounts[0].account.borrow_mut().data = vec![0; 5]; assert_eq!( Err(InstructionError::AccountDataTooSmall), process_instruction(&program_id, &mut keyed_accounts, &instruction_data) @@ -268,8 +309,8 @@ mod tests { let mut elf = Vec::new(); let rent = rent::Rent::default(); file.read_to_end(&mut elf).unwrap(); - let mut program_account = Account::new(rent.minimum_balance(elf.len()), 0, &program_id); - program_account.data = elf; + let mut program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id); + program_account.borrow_mut().data = elf; let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)]; let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap(); @@ -279,7 +320,7 @@ mod tests { process_instruction(&program_id, &mut vec![], &instruction_data) ); - let mut rent_account = rent::create_account(1, &rent); + let mut rent_account = RefCell::new(rent::create_account(1, &rent)); keyed_accounts.push(KeyedAccount::new(&rent_key, false, &mut rent_account)); // Case: Not signed @@ -297,11 +338,12 @@ mod tests { Ok(()), process_instruction(&program_id, &mut keyed_accounts, &instruction_data) ); - assert!(keyed_accounts[0].account.executable); - program_account.executable = false; // Un-finalize the account + assert!(keyed_accounts[0].account.borrow().executable); + + program_account.borrow_mut().executable = false; // Un-finalize the account // Case: Finalize - program_account.data[0] = 0; // bad elf + program_account.borrow_mut().data[0] = 0; // bad elf let mut keyed_accounts = vec![ KeyedAccount::new(&program_key, true, &mut program_account), KeyedAccount::new(&rent_key, false, &mut rent_account), @@ -314,6 +356,8 @@ mod tests { #[test] fn test_bpf_loader_invoke_main() { + solana_logger::setup(); + let program_id = Pubkey::new_rand(); let program_key = Pubkey::new_rand(); @@ -321,9 +365,9 @@ mod tests { let mut file = File::open("test_elfs/noop.so").expect("file open failed"); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - let mut program_account = Account::new(1, 0, &program_id); - program_account.data = elf; - program_account.executable = true; + let mut program_account = Account::new_ref(1, 0, &program_id); + program_account.borrow_mut().data = elf; + program_account.borrow_mut().executable = true; let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &mut program_account)]; @@ -340,15 +384,15 @@ mod tests { ); // Case: Account not executable - keyed_accounts[0].account.executable = false; + keyed_accounts[0].account.borrow_mut().executable = false; assert_eq!( Err(InstructionError::InvalidInstructionData), process_instruction(&program_id, &mut keyed_accounts, &vec![]) ); - keyed_accounts[0].account.executable = true; + keyed_accounts[0].account.borrow_mut().executable = true; // Case: With program and parameter account - let mut parameter_account = Account::new(1, 0, &program_id); + let mut parameter_account = Account::new_ref(1, 0, &program_id); keyed_accounts.push(KeyedAccount::new( &program_key, false, @@ -358,5 +402,15 @@ mod tests { Ok(()), process_instruction(&program_id, &mut keyed_accounts, &vec![]) ); + + // Case: With duplicate accounts + let duplicate_key = Pubkey::new_rand(); + let parameter_account = Account::new_ref(1, 0, &program_id); + keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); + keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); + assert_eq!( + Ok(()), + process_instruction(&program_id, &mut keyed_accounts, &vec![]) + ); } } diff --git a/programs/bpf_loader/test_elfs/noop.so b/programs/bpf_loader/test_elfs/noop.so index 1259bfa04ae58cdd2f5d4857a6412d8312a0af1f..751dc72c8ba11c6598b921dd0db34a9f2d415336 100755 GIT binary patch literal 52344 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 NU$%N9>AS!F{|9qFD)9gS literal 4384 zcmb_gO>7fK6rLo2O@W4{4T4lu+e4*PRn|^$5}RHUN)e$hZIMy|Y8BQ$u}y7n@NP;L zADUD`imDz`sa1ODswZpTIz)(#~gjBL@Pn79!TGt_nujXgj_Ph`@R1+@6GJY zhHpMhpXu%CQ8N0}KMJ`H3#J%14lw~RrIM--o`=){ZMzp_VsE0_$9{r`iN_p!(2wON zuCbe-V!vLHqsvl+3`|V+kz6oJtZE%0;wU6O#=aX#pBy*KnwV|}PWc=;~jx%(QiuV;+D4niF1tMsXUb(Q>XENkrlTBlPj zhp_(#!fWUZL;8yAqzw!JU6KAFOXsT|>8sZ#-Z*^``*yvB2XxV}0lsvcIuLc(tt&=l!11 zM||}pzwE!A<{kV>>m+^{_X{H~vZ8OT00Uz;`=2uM9fbIjFIeY3?Xb?W4&sM(9DHfq z=llIT*jtb{xJ~;txI=x%{QxRe88`q>*k}A+@76y2^5-Yr^^~(s=YhPKO6rlk!5!KM zt!rqtd+QL~rhSF)AJ!YJ)BZhv1UaSlD(VaNHD~m70Q4`+v$3q}!sE{Zz3dz5v-CB% zP49VS;8n1C*5B*w*W_H?eskSBzW2Tk*PT}c;$S7mm!zJ@dVZ}nvipfZS=Na4$@OZ! zrbevui;@s04v%Nb2-DIt+immEmeKxx#hXCTbSz- z6A8Ts@M=JRbkWzvl!EURrB?oUdf^cu4lYNX)dtV_hX%jK_l($g4E`cFMD%wJ{t7o- z^ii&(%k#QoJCVG?`GtRE@D0u{dj0VPe!?$se&O2&zr^zqy~Xnp{(dMQlEa~V$Zr_@ zR46{`8wP(iln?c7gTEKbk9u^+AEyC|`l1Pbpk2CD{R-YKE1f=VCGEH!w}y+pzu=vW z#R?_AxR|lCZY2gg#Hl(XWhdjs%H^C5D$7YE#uM||iJUX)j3+0OlbOj>ZZta{PmE`r z2`81yoS01HPo&-rr+k6Nwc4Zp(q~LqiR;<}yH3u~H&}_^mZ(_`7X}ji7D0KG`=(iV zysZh4bALomv+7aHhJ2Lm4|9LTKE-m`ha#V5`I=!r!}5nl{LfgvfQ5&~MNRi83l}9c z<}}@-;)Z;l<*SDLdzAO~b-(Ae89XZR+v*%x0{K?w#>zHwW&8OhU)eR6PW4<+btgx7agw%rui~z6|=CPDscEH8_azt16YK^oG3ztw**`ecG?DwomA;l zr{Z&?V~i&l6O0p#DZ;Th<0vEBv-u=RIcQ4la1PF$zI1;2O;4-Dp(*T?*O?*SXjc9#6INAUi`yHK{B+>e4+ zLjIUr{N>)XK!&-+U+zyq)Q5*T|6x!*fxBA#+nhkqVrMx9B)`POzTq7&{v9?Dyu}Gk zf2sd{@WtMUzuez~I6oZXZ{jrQFhv}VLgOx8958YO-KW$qcn&m@yK4l*QT{+_v!Q$^ oNd40P3t%g2ANJos2N>JsCoTNV1H@MTr}_Ug { let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let mut budget_state = BudgetState::deserialize(&contract_keyed_account.account.data)?; + let mut budget_state = + BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data)?; if !budget_state.is_pending() { return Ok(()); // Nothing to do here. } @@ -166,12 +168,13 @@ pub fn process_instruction( dt, )?; trace!("apply timestamp committed"); - budget_state.serialize(&mut contract_keyed_account.account.data) + budget_state.serialize(&mut contract_keyed_account.try_account_ref_mut()?.data) } BudgetInstruction::ApplySignature => { let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let mut budget_state = BudgetState::deserialize(&contract_keyed_account.account.data)?; + let mut budget_state = + BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data)?; if !budget_state.is_pending() { return Ok(()); // Nothing to do here. } @@ -190,12 +193,13 @@ pub fn process_instruction( next_keyed_account(keyed_accounts_iter), )?; trace!("apply signature committed"); - budget_state.serialize(&mut contract_keyed_account.account.data) + budget_state.serialize(&mut contract_keyed_account.try_account_ref_mut()?.data) } BudgetInstruction::ApplyAccountData => { let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let mut budget_state = BudgetState::deserialize(&contract_keyed_account.account.data)?; + let mut budget_state = + BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data)?; if !budget_state.is_pending() { return Ok(()); // Nothing to do here. } @@ -210,7 +214,7 @@ pub fn process_instruction( next_keyed_account(keyed_accounts_iter), )?; trace!("apply account data committed"); - budget_state.serialize(&mut contract_keyed_account.account.data) + budget_state.serialize(&mut contract_keyed_account.try_account_ref_mut()?.data) } } } diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index e4f05497db..ca775c7ccc 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -14,18 +14,20 @@ pub fn process_instruction( data: &[u8], ) -> Result<(), InstructionError> { let key_list: ConfigKeys = limited_deserialize(data)?; - let keyed_accounts_iter = &mut keyed_accounts.iter_mut(); + let keyed_accounts_iter = &mut keyed_accounts.iter(); let config_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; - let current_data: ConfigKeys = - deserialize(&config_keyed_account.account.data).map_err(|err| { + let current_data: ConfigKeys = { + let config_account = config_keyed_account.try_account_ref_mut()?; + deserialize(&config_account.data).map_err(|err| { error!( "Unable to deserialize account[0]: {:?} (len={}): {:?}", - config_keyed_account.account.data, - config_keyed_account.account.data.len(), + config_account.data, + config_account.data.len(), err ); InstructionError::InvalidAccountData - })?; + })? + }; let current_signer_keys: Vec = current_data .keys .iter() @@ -89,12 +91,12 @@ pub fn process_instruction( return Err(InstructionError::MissingRequiredSignature); } - if config_keyed_account.account.data.len() < data.len() { + if config_keyed_account.data_len()? < data.len() { error!("instruction data too large"); return Err(InstructionError::InvalidInstructionData); } - config_keyed_account.account.data[0..data.len()].copy_from_slice(&data); + config_keyed_account.try_account_ref_mut()?.data[0..data.len()].copy_from_slice(&data); Ok(()) } @@ -109,6 +111,7 @@ mod tests { signature::{Keypair, KeypairUtil}, system_instruction::SystemInstruction, }; + use std::cell::RefCell; #[derive(Serialize, Deserialize, Debug, PartialEq)] struct MyConfig { @@ -134,7 +137,7 @@ mod tests { } } - fn create_config_account(keys: Vec<(Pubkey, bool)>) -> (Keypair, Account) { + fn create_config_account(keys: Vec<(Pubkey, bool)>) -> (Keypair, RefCell) { let from_pubkey = Pubkey::new_rand(); let config_keypair = Keypair::new(); let config_pubkey = config_keypair.pubkey(); @@ -151,10 +154,10 @@ mod tests { } => space, _ => panic!("Not a CreateAccount system instruction"), }; - let mut config_account = Account { + let mut config_account = RefCell::new(Account { data: vec![0; space as usize], ..Account::default() - }; + }); let mut accounts = vec![(&config_pubkey, true, &mut config_account)]; let mut keyed_accounts = create_keyed_is_signer_accounts(&mut accounts); assert_eq!( @@ -172,7 +175,7 @@ mod tests { let (_, config_account) = create_config_account(keys.clone()); assert_eq!( Some(MyConfig::default()), - deserialize(get_config_data(&config_account.data).unwrap()).ok() + deserialize(get_config_data(&config_account.borrow().data).unwrap()).ok() ); } @@ -193,7 +196,7 @@ mod tests { ); assert_eq!( Some(my_config), - deserialize(get_config_data(&config_account.data).unwrap()).ok() + deserialize(get_config_data(&config_account.borrow().data).unwrap()).ok() ); } @@ -250,8 +253,8 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let mut signer0_account = Account::default(); - let mut signer1_account = Account::default(); + let mut signer0_account = RefCell::new(Account::default()); + let mut signer1_account = RefCell::new(Account::default()); let mut accounts = vec![ (&config_pubkey, true, &mut config_account), (&signer0_pubkey, true, &mut signer0_account), @@ -262,11 +265,11 @@ mod tests { process_instruction(&id(), &mut keyed_accounts, &instruction.data), Ok(()) ); - let meta_data: ConfigKeys = deserialize(&config_account.data).unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account.borrow().data).unwrap(); assert_eq!(meta_data.keys, keys); assert_eq!( Some(my_config), - deserialize(get_config_data(&config_account.data).unwrap()).ok() + deserialize(get_config_data(&config_account.borrow().data).unwrap()).ok() ); } @@ -282,7 +285,7 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); - let mut signer0_account = Account::default(); + let mut signer0_account = RefCell::new(Account::default()); let mut accounts = vec![(&signer0_pubkey, true, &mut signer0_account)]; let mut keyed_accounts = create_keyed_is_signer_accounts(&mut accounts); assert_eq!( @@ -296,8 +299,8 @@ mod tests { solana_logger::setup(); let signer0_pubkey = Pubkey::new_rand(); let signer1_pubkey = Pubkey::new_rand(); - let mut signer0_account = Account::default(); - let mut signer1_account = Account::default(); + let mut signer0_account = RefCell::new(Account::default()); + let mut signer1_account = RefCell::new(Account::default()); let keys = vec![(signer0_pubkey, true)]; let (config_keypair, mut config_account) = create_config_account(keys.clone()); let config_pubkey = config_keypair.pubkey(); @@ -335,9 +338,9 @@ mod tests { let signer0_pubkey = Pubkey::new_rand(); let signer1_pubkey = Pubkey::new_rand(); let signer2_pubkey = Pubkey::new_rand(); - let mut signer0_account = Account::default(); - let mut signer1_account = Account::default(); - let mut signer2_account = Account::default(); + let mut signer0_account = RefCell::new(Account::default()); + let mut signer1_account = RefCell::new(Account::default()); + let mut signer2_account = RefCell::new(Account::default()); let keys = vec![ (pubkey, false), (signer0_pubkey, true), @@ -373,11 +376,11 @@ mod tests { process_instruction(&id(), &mut keyed_accounts, &instruction.data), Ok(()) ); - let meta_data: ConfigKeys = deserialize(&config_account.data).unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account.borrow().data).unwrap(); assert_eq!(meta_data.keys, keys); assert_eq!( new_config, - MyConfig::deserialize(get_config_data(&config_account.data).unwrap()).unwrap() + MyConfig::deserialize(get_config_data(&config_account.borrow().data).unwrap()).unwrap() ); // Attempt update with incomplete signatures @@ -420,7 +423,7 @@ mod tests { solana_logger::setup(); let pubkey = Pubkey::new_rand(); let signer0_pubkey = Pubkey::new_rand(); - let mut signer0_account = Account::default(); + let mut signer0_account = RefCell::new(Account::default()); let keys = vec![ (pubkey, false), (signer0_pubkey, true), @@ -460,11 +463,11 @@ mod tests { process_instruction(&id(), &mut keyed_accounts, &instruction.data), Ok(()) ); - let meta_data: ConfigKeys = deserialize(&config_account.data).unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account.borrow().data).unwrap(); assert_eq!(meta_data.keys, keys); assert_eq!( new_config, - MyConfig::deserialize(get_config_data(&config_account.data).unwrap()).unwrap() + MyConfig::deserialize(get_config_data(&config_account.borrow().data).unwrap()).unwrap() ); // Attempt update with incomplete signatures diff --git a/programs/exchange/src/exchange_processor.rs b/programs/exchange/src/exchange_processor.rs index a8a3f53086..f299145164 100644 --- a/programs/exchange/src/exchange_processor.rs +++ b/programs/exchange/src/exchange_processor.rs @@ -164,15 +164,16 @@ impl ExchangeProcessor { error!("Not enough accounts"); return Err(InstructionError::InvalidArgument); } - - Self::is_account_unallocated(&keyed_accounts[NEW_ACCOUNT_INDEX].account.data)?; + Self::is_account_unallocated(&keyed_accounts[NEW_ACCOUNT_INDEX].try_account_ref()?.data)?; Self::serialize( &ExchangeState::Account( TokenAccountInfo::default() .owner(&keyed_accounts[OWNER_INDEX].unsigned_key()) .tokens(100_000, 100_000, 100_000, 100_000), ), - &mut keyed_accounts[NEW_ACCOUNT_INDEX].account.data, + &mut keyed_accounts[NEW_ACCOUNT_INDEX] + .try_account_ref_mut()? + .data, ) } @@ -191,13 +192,13 @@ impl ExchangeProcessor { } let mut to_account = - Self::deserialize_account(&keyed_accounts[TO_ACCOUNT_INDEX].account.data)?; + Self::deserialize_account(&keyed_accounts[TO_ACCOUNT_INDEX].try_account_ref()?.data)?; if &faucet::id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() { to_account.tokens[token] += tokens; } else { let state: ExchangeState = - bincode::deserialize(&keyed_accounts[FROM_ACCOUNT_INDEX].account.data) + bincode::deserialize(&keyed_accounts[FROM_ACCOUNT_INDEX].try_account_ref()?.data) .map_err(Self::map_to_invalid_arg)?; match state { ExchangeState::Account(mut from_account) => { @@ -216,7 +217,9 @@ impl ExchangeProcessor { Self::serialize( &ExchangeState::Account(from_account), - &mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data, + &mut keyed_accounts[FROM_ACCOUNT_INDEX] + .try_account_ref_mut()? + .data, )?; } ExchangeState::Trade(mut from_trade) => { @@ -244,7 +247,9 @@ impl ExchangeProcessor { Self::serialize( &ExchangeState::Trade(from_trade), - &mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data, + &mut keyed_accounts[FROM_ACCOUNT_INDEX] + .try_account_ref_mut()? + .data, )?; } _ => { @@ -256,7 +261,7 @@ impl ExchangeProcessor { Self::serialize( &ExchangeState::Account(to_account), - &mut keyed_accounts[TO_ACCOUNT_INDEX].account.data, + &mut keyed_accounts[TO_ACCOUNT_INDEX].try_account_ref_mut()?.data, ) } @@ -273,9 +278,10 @@ impl ExchangeProcessor { return Err(InstructionError::InvalidArgument); } - Self::is_account_unallocated(&keyed_accounts[ORDER_INDEX].account.data)?; + Self::is_account_unallocated(&keyed_accounts[ORDER_INDEX].try_account_ref()?.data)?; - let mut account = Self::deserialize_account(&keyed_accounts[ACCOUNT_INDEX].account.data)?; + let mut account = + Self::deserialize_account(&keyed_accounts[ACCOUNT_INDEX].try_account_ref_mut()?.data)?; if &account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() { error!("Signer does not own account"); @@ -308,11 +314,11 @@ impl ExchangeProcessor { price: info.price, tokens_settled: 0, }), - &mut keyed_accounts[ORDER_INDEX].account.data, + &mut keyed_accounts[ORDER_INDEX].try_account_ref_mut()?.data, )?; Self::serialize( &ExchangeState::Account(account), - &mut keyed_accounts[ACCOUNT_INDEX].account.data, + &mut keyed_accounts[ACCOUNT_INDEX].try_account_ref_mut()?.data, ) } @@ -325,7 +331,7 @@ impl ExchangeProcessor { return Err(InstructionError::InvalidArgument); } - let order = Self::deserialize_order(&keyed_accounts[ORDER_INDEX].account.data)?; + let order = Self::deserialize_order(&keyed_accounts[ORDER_INDEX].try_account_ref()?.data)?; if &order.owner != keyed_accounts[OWNER_INDEX].unsigned_key() { error!("Signer does not own trade"); @@ -344,7 +350,7 @@ impl ExchangeProcessor { // Turn trade order into a token account Self::serialize( &ExchangeState::Account(account), - &mut keyed_accounts[ORDER_INDEX].account.data, + &mut keyed_accounts[ORDER_INDEX].try_account_ref_mut()?.data, ) } @@ -358,11 +364,13 @@ impl ExchangeProcessor { return Err(InstructionError::InvalidArgument); } - let mut to_order = Self::deserialize_order(&keyed_accounts[TO_ORDER_INDEX].account.data)?; + let mut to_order = + Self::deserialize_order(&keyed_accounts[TO_ORDER_INDEX].try_account_ref()?.data)?; let mut from_order = - Self::deserialize_order(&keyed_accounts[FROM_ORDER_INDEX].account.data)?; - let mut profit_account = - Self::deserialize_account(&keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data)?; + Self::deserialize_order(&keyed_accounts[FROM_ORDER_INDEX].try_account_ref()?.data)?; + let mut profit_account = Self::deserialize_account( + &keyed_accounts[PROFIT_ACCOUNT_INDEX].try_account_ref()?.data, + )?; if to_order.side != OrderSide::Ask { error!("To trade is not a To"); @@ -397,12 +405,12 @@ impl ExchangeProcessor { // Turn into token account Self::serialize( &ExchangeState::Account(Self::trade_to_token_account(&from_order)), - &mut keyed_accounts[TO_ORDER_INDEX].account.data, + &mut keyed_accounts[TO_ORDER_INDEX].try_account_ref_mut()?.data, )?; } else { Self::serialize( &ExchangeState::Trade(to_order), - &mut keyed_accounts[TO_ORDER_INDEX].account.data, + &mut keyed_accounts[TO_ORDER_INDEX].try_account_ref_mut()?.data, )?; } @@ -410,18 +418,20 @@ impl ExchangeProcessor { // Turn into token account Self::serialize( &ExchangeState::Account(Self::trade_to_token_account(&from_order)), - &mut keyed_accounts[FROM_ORDER_INDEX].account.data, + &mut keyed_accounts[FROM_ORDER_INDEX].try_account_ref_mut()?.data, )?; } else { Self::serialize( &ExchangeState::Trade(from_order), - &mut keyed_accounts[FROM_ORDER_INDEX].account.data, + &mut keyed_accounts[FROM_ORDER_INDEX].try_account_ref_mut()?.data, )?; } Self::serialize( &ExchangeState::Account(profit_account), - &mut keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data, + &mut keyed_accounts[PROFIT_ACCOUNT_INDEX] + .try_account_ref_mut()? + .data, ) } } diff --git a/programs/move_loader/src/processor.rs b/programs/move_loader/src/processor.rs index cf694d9ec9..76662b1a01 100644 --- a/programs/move_loader/src/processor.rs +++ b/programs/move_loader/src/processor.rs @@ -11,6 +11,7 @@ use solana_sdk::{ move_loader::id, pubkey::Pubkey, sysvar::rent, + account_utils::State, }; use types::{ account_address::AccountAddress, @@ -177,7 +178,7 @@ impl MoveProcessor { let mut keyed_accounts_iter = keyed_accounts.iter(); let genesis = next_keyed_account(&mut keyed_accounts_iter)?; - match limited_deserialize(&genesis.account.data)? { + match limited_deserialize(&genesis.try_account_ref()?.data)? { LibraAccountState::Genesis(write_set) => data_store.apply_write_set(&write_set), _ => { debug!("Must include a genesis account"); @@ -186,7 +187,7 @@ impl MoveProcessor { } for keyed_account in keyed_accounts_iter { - match limited_deserialize(&keyed_account.account.data)? { + match limited_deserialize(&keyed_account.try_account_ref()?.data)? { LibraAccountState::User(owner, write_set) => { if owner != *genesis.unsigned_key() { debug!("User account must be owned by this genesis"); @@ -230,7 +231,7 @@ impl MoveProcessor { let write_set = write_set.freeze().unwrap(); Self::serialize_and_enforce_length( &LibraAccountState::Genesis(write_set), - &mut genesis.account.data, + &mut genesis.try_account_ref_mut()?.data, )?; // Now do the rest of the accounts @@ -238,11 +239,11 @@ impl MoveProcessor { let write_set = write_sets .remove(&pubkey_to_address(keyed_account.unsigned_key())) .ok_or_else(Self::missing_account)?; - if !keyed_account.account.executable { + if !keyed_account.executable()? { // Only write back non-executable accounts Self::serialize_and_enforce_length( &LibraAccountState::User(genesis_key, write_set), - &mut keyed_account.account.data, + &mut keyed_account.try_account_ref_mut()?.data, )?; } } @@ -268,15 +269,15 @@ impl MoveProcessor { let offset = offset as usize; let len = bytes.len(); trace!("Write: offset={} length={}", offset, len); - if keyed_account.account.data.len() < offset + len { + if keyed_account.data_len()? < offset + len { debug!( "Error: Write overflow: {} < {}", - keyed_account.account.data.len(), + keyed_account.data_len()?, offset + len ); return Err(InstructionError::AccountDataTooSmall); } - keyed_account.account.data[offset..offset + len].copy_from_slice(&bytes); + keyed_account.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes); Ok(()) } @@ -292,7 +293,7 @@ impl MoveProcessor { rent::verify_rent_exemption(&finalized, &rent)?; - match limited_deserialize(&finalized.account.data)? { + match finalized.state()? { LibraAccountState::CompiledScript(string) => { let script: Script = serde_json::from_str(&string).map_err(map_json_error)?; let compiled_script = @@ -314,7 +315,7 @@ impl MoveProcessor { .map_err(map_failure_error)?; Self::serialize_and_enforce_length( &LibraAccountState::VerifiedScript { script_bytes }, - &mut finalized.account.data, + &mut finalized.try_account_ref_mut()?.data, )?; info!("Finalize script: {:?}", finalized.unsigned_key()); } @@ -339,7 +340,7 @@ impl MoveProcessor { .ok_or_else(Self::missing_account)?; Self::serialize_and_enforce_length( &LibraAccountState::PublishedModule(write_set), - &mut finalized.account.data, + &mut finalized.try_account_ref_mut()?.data, )?; if !write_sets.is_empty() { @@ -355,7 +356,7 @@ impl MoveProcessor { } }; - finalized.account.executable = true; + finalized.try_account_ref_mut()?.executable = true; Ok(()) } @@ -365,17 +366,17 @@ impl MoveProcessor { amount: u64, ) -> Result<(), InstructionError> { let mut keyed_accounts_iter = keyed_accounts.iter_mut(); - let script = next_keyed_account(&mut keyed_accounts_iter)?; + let genesis = next_keyed_account(&mut keyed_accounts_iter)?; - if script.account.owner != id() { - debug!("Error: Move script account not owned by Move loader"); + if genesis.owner()? != id() { + debug!("Error: Move genesis account not owned by Move loader"); return Err(InstructionError::InvalidArgument); } - match limited_deserialize(&script.account.data)? { + match genesis.state()? { LibraAccountState::Unallocated => Self::serialize_and_enforce_length( &LibraAccountState::create_genesis(amount)?, - &mut script.account.data, + &mut genesis.try_account_ref_mut()?.data, ), _ => { debug!("Error: Must provide an unallocated account"); @@ -399,11 +400,11 @@ impl MoveProcessor { function_name ); - if script.account.owner != id() { + if script.owner()? != id() { debug!("Error: Move script account not owned by Move loader"); return Err(InstructionError::InvalidArgument); } - if !script.account.executable { + if !script.executable()? { debug!("Error: Move script account not executable"); return Err(InstructionError::AccountNotExecutable); } @@ -411,7 +412,7 @@ impl MoveProcessor { let data_accounts = keyed_accounts_iter.into_slice(); let mut data_store = Self::keyed_accounts_to_data_store(&data_accounts)?; - let verified_script = Self::deserialize_verified_script(&script.account.data)?; + let verified_script = Self::deserialize_verified_script(&script.try_account_ref()?.data)?; let output = Self::execute( sender_address, @@ -435,7 +436,7 @@ impl MoveProcessor { ) -> Result<(), InstructionError> { solana_logger::setup(); - if is_executable(keyed_accounts) { + if is_executable(keyed_accounts)? { match limited_deserialize(&instruction_data)? { Executable::RunScript { sender_address, @@ -463,6 +464,7 @@ mod tests { use solana_sdk::account::Account; use solana_sdk::rent::Rent; use solana_sdk::sysvar::rent; + use std::cell::RefCell; const BIG_ENOUGH: usize = 10_000; @@ -500,13 +502,13 @@ mod tests { let sender_address = AccountAddress::default(); let mut script = LibraAccount::create_script(&sender_address, code, vec![]); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), ]; MoveProcessor::do_finalize(&mut keyed_accounts).unwrap(); - let _ = MoveProcessor::deserialize_verified_script(&script.account.data).unwrap(); + let _ = MoveProcessor::deserialize_verified_script(&script.account.borrow().data).unwrap(); } #[test] @@ -525,10 +527,11 @@ mod tests { assert_eq!( bincode::deserialize::( - &LibraAccount::create_genesis(amount).account.data + &LibraAccount::create_genesis(amount).account.borrow().data ) .unwrap(), - bincode::deserialize::(&keyed_accounts[0].account.data).unwrap() + bincode::deserialize::(&keyed_accounts[0].account.borrow().data) + .unwrap() ); } @@ -542,7 +545,7 @@ mod tests { let mut genesis = LibraAccount::create_genesis(1_000_000_000); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), @@ -577,12 +580,16 @@ mod tests { "; let mut module = LibraAccount::create_module(code, vec![]); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&module.key, true, &mut module.account), KeyedAccount::new(&rent_id, false, &mut rent_account), ]; - keyed_accounts[0].account.data.resize(BIG_ENOUGH, 0); + keyed_accounts[0] + .account + .borrow_mut() + .data + .resize(BIG_ENOUGH, 0); MoveProcessor::do_finalize(&mut keyed_accounts).unwrap(); } @@ -602,7 +609,7 @@ mod tests { let mut genesis = LibraAccount::create_genesis(1_000_000_000); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), @@ -639,7 +646,7 @@ mod tests { let _script = next_libra_account(&mut accounts_iter).unwrap(); let genesis = next_libra_account(&mut accounts_iter).unwrap(); let payee = next_libra_account(&mut accounts_iter).unwrap(); - match bincode::deserialize(&payee.account.data).unwrap() { + match bincode::deserialize(&payee.account.borrow().data).unwrap() { LibraAccountState::User(owner, write_set) => { if owner != genesis.key { panic!(); @@ -677,7 +684,7 @@ mod tests { let mut payee = LibraAccount::create_unallocated(BIG_ENOUGH); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), @@ -735,12 +742,16 @@ mod tests { let mut module = LibraAccount::create_module(&code, vec![]); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&module.key, true, &mut module.account), KeyedAccount::new(&rent_id, false, &mut rent_account), ]; - keyed_accounts[0].account.data.resize(BIG_ENOUGH, 0); + keyed_accounts[0] + .account + .borrow_mut() + .data + .resize(BIG_ENOUGH, 0); MoveProcessor::do_finalize(&mut keyed_accounts).unwrap(); @@ -769,12 +780,15 @@ mod tests { ", module.address ); - let mut script = - LibraAccount::create_script(&genesis.address, &code, vec![&module.account.data]); + let mut script = LibraAccount::create_script( + &genesis.address, + &code, + vec![&module.account.borrow().data], + ); let mut payee = LibraAccount::create_unallocated(BIG_ENOUGH); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), @@ -824,7 +838,7 @@ mod tests { let mut payee = LibraAccount::create_unallocated(BIG_ENOUGH); let rent_id = rent::id(); - let mut rent_account = rent::create_account(1, &Rent::free()); + let mut rent_account = RefCell::new(rent::create_account(1, &Rent::free())); let mut keyed_accounts = vec![ KeyedAccount::new(&script.key, true, &mut script.account), KeyedAccount::new(&rent_id, false, &mut rent_account), @@ -850,9 +864,9 @@ mod tests { .unwrap(); Ok(vec![ - LibraAccount::new(script.key, script.account), - LibraAccount::new(genesis.key, genesis.account), - LibraAccount::new(payee.key, payee.account), + LibraAccount::new(script.key, script.account.into_inner()), + LibraAccount::new(genesis.key, genesis.account.into_inner()), + LibraAccount::new(payee.key, payee.account.into_inner()), ]) } @@ -860,7 +874,7 @@ mod tests { struct LibraAccount { pub key: Pubkey, pub address: AccountAddress, - pub account: Account, + pub account: RefCell, } pub fn next_libra_account(iter: &mut I) -> Result { @@ -873,7 +887,7 @@ mod tests { Self { key, address, - account, + account: RefCell::new(account), } } @@ -898,10 +912,10 @@ mod tests { owner: id(), ..Account::default() }; - let mut genesis = Self::new(Pubkey::new_rand(), account); + let genesis = Self::new(Pubkey::new_rand(), account); let pre_data = LibraAccountState::create_genesis(amount).unwrap(); let _hi = "hello"; - genesis.account.data = bincode::serialize(&pre_data).unwrap(); + genesis.account.borrow_mut().data = bincode::serialize(&pre_data).unwrap(); genesis } @@ -910,24 +924,20 @@ mod tests { code: &str, deps: Vec<&Vec>, ) -> Self { - let mut script = Self::create_unallocated(0); - script.account.data = bincode::serialize(&LibraAccountState::create_script( - sender_address, - code, - deps, - )) + let script = Self::create_unallocated(0); + script.account.borrow_mut().data = bincode::serialize( + &LibraAccountState::create_script(sender_address, code, deps), + ) .unwrap(); script } pub fn create_module(code: &str, deps: Vec<&Vec>) -> Self { - let mut module = Self::create_unallocated(0); - module.account.data = bincode::serialize(&LibraAccountState::create_module( - &module.address, - code, - deps, - )) + let module = Self::create_unallocated(0); + module.account.borrow_mut().data = bincode::serialize( + &LibraAccountState::create_module(&module.address, code, deps), + ) .unwrap(); module diff --git a/programs/ownable/src/ownable_processor.rs b/programs/ownable/src/ownable_processor.rs index 9cf9bafdef..d7282bf77a 100644 --- a/programs/ownable/src/ownable_processor.rs +++ b/programs/ownable/src/ownable_processor.rs @@ -35,7 +35,7 @@ pub fn process_instruction( let keyed_accounts_iter = &mut keyed_accounts.iter_mut(); let account_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; let mut account_owner_pubkey: Pubkey = - limited_deserialize(&account_keyed_account.account.data)?; + limited_deserialize(&account_keyed_account.try_account_ref()?.data)?; if account_owner_pubkey == Pubkey::default() { account_owner_pubkey = new_owner_pubkey; @@ -48,11 +48,9 @@ pub fn process_instruction( )?; } - serialize_into( - &mut account_keyed_account.account.data[..], - &account_owner_pubkey, - ) - .map_err(|_| InstructionError::AccountDataTooSmall) + let mut account = account_keyed_account.try_account_ref_mut()?; + serialize_into(&mut account.data[..], &account_owner_pubkey) + .map_err(|_| InstructionError::AccountDataTooSmall) } #[cfg(test)] @@ -156,7 +154,7 @@ mod tests { let mut account_owner_pubkey = Pubkey::new_rand(); let owner_pubkey = account_owner_pubkey; let new_owner_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &system_program::id()); + let mut account = Account::new_ref(1, 0, &system_program::id()); let owner_keyed_account = KeyedAccount::new(&owner_pubkey, false, &mut account); // <-- Attack! Setting owner without the original owner's signature. let err = set_owner( &mut account_owner_pubkey, @@ -171,7 +169,7 @@ mod tests { fn test_ownable_incorrect_owner() { let mut account_owner_pubkey = Pubkey::new_rand(); let new_owner_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &system_program::id()); + let mut account = Account::new_ref(1, 0, &system_program::id()); let mallory_pubkey = Pubkey::new_rand(); // <-- Attack! Signing with wrong pubkey let owner_keyed_account = KeyedAccount::new(&mallory_pubkey, true, &mut account); let err = set_owner( diff --git a/programs/stake/src/config.rs b/programs/stake/src/config.rs index 6393bdd57f..d79b779b79 100644 --- a/programs/stake/src/config.rs +++ b/programs/stake/src/config.rs @@ -67,18 +67,19 @@ pub fn from_keyed_account(account: &KeyedAccount) -> Result RefCell { + RefCell::new(Account::default()) + } fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { let mut accounts: Vec<_> = instruction .accounts .iter() .map(|meta| { - if sysvar::clock::check_id(&meta.pubkey) { + RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { sysvar::clock::Clock::default().create_account(1) } else if sysvar::rewards::check_id(&meta.pubkey) { sysvar::rewards::create_account(1, 0.0, 0.0) @@ -466,7 +471,7 @@ mod tests { sysvar::rent::create_account(1, &Rent::default()) } else { Account::default() - } + }) }) .collect(); @@ -576,7 +581,7 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), false, - &mut Account::default(), + &mut create_default_account(), )], &serialize(&StakeInstruction::Initialize( Authorized::default(), @@ -592,8 +597,8 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default(),), - KeyedAccount::new(&sysvar::rent::id(), false, &mut Account::default(),) + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account(),), + KeyedAccount::new(&sysvar::rent::id(), false, &mut create_default_account(),) ], &serialize(&StakeInstruction::Initialize( Authorized::default(), @@ -609,11 +614,11 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default(),), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), KeyedAccount::new( &sysvar::rent::id(), false, - &mut sysvar::rent::create_account(0, &Rent::default()) + &mut RefCell::new(sysvar::rent::create_account(0, &Rent::default())) ) ], &serialize(&StakeInstruction::Initialize( @@ -632,8 +637,8 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), false, - &mut Account::default(), - )], + &mut create_default_account() + ),], &serialize(&StakeInstruction::DelegateStake).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -646,8 +651,8 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), false, - &mut Account::default() - ),], + &mut create_default_account() + )], &serialize(&StakeInstruction::DelegateStake).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -658,8 +663,8 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), ], &serialize(&StakeInstruction::RedeemVoteCredits).unwrap(), ), @@ -671,11 +676,11 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), ], &serialize(&StakeInstruction::RedeemVoteCredits).unwrap(), ), @@ -687,17 +692,17 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), true, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), KeyedAccount::new( &sysvar::clock::id(), false, - &mut sysvar::clock::Clock::default().create_account(1) + &mut RefCell::new(sysvar::clock::Clock::default().create_account(1)) ), KeyedAccount::new( &config::id(), false, - &mut config::create_account(0, &config::Config::default()) + &mut RefCell::new(config::create_account(0, &config::Config::default())) ), ], &serialize(&StakeInstruction::DelegateStake).unwrap(), @@ -710,18 +715,21 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), KeyedAccount::new( &sysvar::rewards::id(), false, - &mut sysvar::rewards::create_account(1, 0.0, 0.0) + &mut RefCell::new(sysvar::rewards::create_account(1, 0.0, 0.0)) ), KeyedAccount::new( &sysvar::stake_history::id(), false, - &mut sysvar::stake_history::create_account(1, &StakeHistory::default()) + &mut RefCell::new(sysvar::stake_history::create_account( + 1, + &StakeHistory::default() + )) ), ], &serialize(&StakeInstruction::RedeemVoteCredits).unwrap(), @@ -734,17 +742,20 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), KeyedAccount::new( &sysvar::rewards::id(), false, - &mut sysvar::rewards::create_account(1, 0.0, 0.0) + &mut RefCell::new(sysvar::rewards::create_account(1, 0.0, 0.0)) ), KeyedAccount::new( &sysvar::stake_history::id(), false, - &mut sysvar::stake_history::create_account(1, &StakeHistory::default()) + &mut RefCell::new(sysvar::stake_history::create_account( + 1, + &StakeHistory::default() + )) ), ], &serialize(&StakeInstruction::Withdraw(42)).unwrap(), @@ -759,7 +770,7 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), false, - &mut Account::default() + &mut create_default_account() )], &serialize(&StakeInstruction::Withdraw(42)).unwrap(), ), @@ -771,11 +782,11 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut create_default_account()), KeyedAccount::new( &sysvar::rewards::id(), false, - &mut sysvar::rewards::create_account(1, 0.0, 0.0) + &mut RefCell::new(sysvar::rewards::create_account(1, 0.0, 0.0)) ), ], &serialize(&StakeInstruction::Deactivate).unwrap(), diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 30628e285f..9f394decc3 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -553,9 +553,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> { rent: &Rent, ) -> Result<(), InstructionError> { if let StakeState::Uninitialized = self.state()? { - let rent_exempt_reserve = rent.minimum_balance(self.account.data.len()); + let rent_exempt_reserve = rent.minimum_balance(self.data_len()?); - if rent_exempt_reserve < self.account.lamports { + if rent_exempt_reserve < self.lamports()? { self.set_state(&StakeState::Initialized(Meta { rent_exempt_reserve, authorized: *authorized, @@ -602,9 +602,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { StakeState::Initialized(meta) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; let stake = Stake::new( - self.account - .lamports - .saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;) + self.lamports()?.saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;) vote_account.unsigned_key(), &vote_account.state()?, clock.epoch, @@ -659,13 +657,13 @@ impl<'a> StakeAccount for KeyedAccount<'a> { Some(stake_history), ) { - if rewards_account.account.lamports < (stakers_reward + voters_reward) { + if rewards_account.lamports()? < (stakers_reward + voters_reward) { return Err(InstructionError::UnbalancedInstruction); } - rewards_account.account.lamports -= stakers_reward + voters_reward; + rewards_account.try_account_ref_mut()?.lamports -= stakers_reward + voters_reward; - self.account.lamports += stakers_reward; - vote_account.account.lamports += voters_reward; + self.try_account_ref_mut()?.lamports += stakers_reward; + vote_account.try_account_ref_mut()?.lamports += voters_reward; stake.credits_observed = credits_observed; stake.delegation.stake += stakers_reward; @@ -688,7 +686,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { ) -> Result<(), InstructionError> { if let StakeState::Uninitialized = split.state()? { // verify enough account lamports - if lamports > self.account.lamports { + if lamports > self.lamports()? { return Err(InstructionError::InsufficientFunds); } @@ -697,9 +695,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> { meta.authorized.check(signers, StakeAuthorize::Staker)?; // verify enough lamports for rent in new stake with the split - if split.account.lamports + lamports < meta.rent_exempt_reserve + if split.lamports()? + lamports < meta.rent_exempt_reserve // verify enough lamports left in previous stake and not full withdrawal - || (lamports + meta.rent_exempt_reserve > self.account.lamports && lamports != self.account.lamports) + || (lamports + meta.rent_exempt_reserve > self.lamports()? && lamports != self.lamports()?) { return Err(InstructionError::InsufficientFunds); } @@ -709,10 +707,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { // this could represent a small loss of staked lamports // if the split account starts out with a zero balance let split_stake = stake.split( - lamports - - meta - .rent_exempt_reserve - .saturating_sub(split.account.lamports), + lamports - meta.rent_exempt_reserve.saturating_sub(split.lamports()?), )?; self.set_state(&StakeState::Stake(meta, stake))?; @@ -724,7 +719,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { // enough lamports for rent in new stake if lamports < meta.rent_exempt_reserve // verify enough lamports left in previous stake - || (lamports + meta.rent_exempt_reserve > self.account.lamports && lamports != self.account.lamports) + || (lamports + meta.rent_exempt_reserve > self.lamports()? && lamports != self.lamports()?) { return Err(InstructionError::InsufficientFunds); } @@ -739,8 +734,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> { _ => return Err(InstructionError::InvalidAccountData), } - split.account.lamports += lamports; - self.account.lamports -= lamports; + split.try_account_ref_mut()?.lamports += lamports; + self.try_account_ref_mut()?.lamports -= lamports; Ok(()) } else { Err(InstructionError::InvalidAccountData) @@ -792,20 +787,20 @@ impl<'a> StakeAccount for KeyedAccount<'a> { // if the stake is active, we mustn't allow the account to go away if is_staked // line coverage for branch coverage - && lamports + reserve > self.account.lamports + && lamports + reserve > self.lamports()? { return Err(InstructionError::InsufficientFunds); } - if lamports != self.account.lamports // not a full withdrawal - && lamports + reserve > self.account.lamports + if lamports != self.lamports()? // not a full withdrawal + && lamports + reserve > self.lamports()? { assert!(!is_staked); return Err(InstructionError::InsufficientFunds); } - self.account.lamports -= lamports; - to.account.lamports += lamports; + self.try_account_ref_mut()?.lamports -= lamports; + to.try_account_ref_mut()?.lamports += lamports; Ok(()) } } @@ -903,6 +898,7 @@ mod tests { use crate::id; use solana_sdk::{account::Account, pubkey::Pubkey, system_program}; use solana_vote_program::vote_state; + use std::cell::RefCell; impl Meta { pub fn auto(authorized: &Pubkey) -> Self { @@ -1004,14 +1000,18 @@ mod tests { vote_state.process_slot_vote_unchecked(i); } - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); vote_keyed_account.set_state(&vote_state).unwrap(); let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Initialized(Meta { authorized: Authorized { @@ -1061,7 +1061,7 @@ mod tests { .is_ok()); // verify that delegate_stake() looks right, compare against hand-rolled - let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap(); + let stake = StakeState::stake_from(&stake_keyed_account.account.borrow()).unwrap(); assert_eq!( stake, Stake { @@ -1462,7 +1462,7 @@ mod tests { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; let mut stake_account = - Account::new(stake_lamports, std::mem::size_of::(), &id()); + Account::new_ref(stake_lamports, std::mem::size_of::(), &id()); // unsigned keyed account let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); @@ -1496,7 +1496,7 @@ mod tests { ); // check that we see what we expect assert_eq!( - StakeState::from(&stake_keyed_account.account).unwrap(), + StakeState::from(&stake_keyed_account.account.borrow()).unwrap(), StakeState::Initialized(Meta { lockup: Lockup { unix_timestamp: 0, @@ -1525,7 +1525,7 @@ mod tests { fn test_deactivate_stake() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -1548,8 +1548,12 @@ mod tests { // Staking let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); vote_keyed_account.set_state(&VoteState::default()).unwrap(); assert_eq!( @@ -1587,7 +1591,7 @@ mod tests { fn test_withdraw_stake() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Uninitialized, std::mem::size_of::(), @@ -1598,7 +1602,7 @@ mod tests { let mut clock = sysvar::clock::Clock::default(); let to = Pubkey::new_rand(); - let mut to_account = Account::new(1, 0, &system_program::id()); + let mut to_account = Account::new_ref(1, 0, &system_program::id()); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); // no signers, should fail @@ -1628,10 +1632,10 @@ mod tests { ), Ok(()) ); - assert_eq!(stake_account.lamports, 0); + assert_eq!(stake_account.borrow().lamports, 0); // reset balance - stake_account.lamports = stake_lamports; + stake_account.borrow_mut().lamports = stake_lamports; // lockup let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); @@ -1664,8 +1668,12 @@ mod tests { // Stake some lamports (available lamports for withdrawals will reduce to zero) let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); vote_keyed_account.set_state(&VoteState::default()).unwrap(); assert_eq!( @@ -1679,7 +1687,7 @@ mod tests { ); // simulate rewards - stake_account.lamports += 10; + stake_account.borrow_mut().lamports += 10; // withdrawal before deactivate works for rewards amount let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); @@ -1695,7 +1703,7 @@ mod tests { ); // simulate rewards - stake_account.lamports += 10; + stake_account.borrow_mut().lamports += 10; // withdrawal of rewards fails if not in excess of stake let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); @@ -1743,7 +1751,7 @@ mod tests { ), Ok(()) ); - assert_eq!(stake_account.lamports, 0); + assert_eq!(stake_account.borrow().lamports, 0); } #[test] @@ -1751,7 +1759,7 @@ mod tests { let stake_pubkey = Pubkey::new_rand(); let total_lamports = 100; let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( total_lamports, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -1764,15 +1772,19 @@ mod tests { future.epoch += 16; let to = Pubkey::new_rand(); - let mut to_account = Account::new(1, 0, &system_program::id()); + let mut to_account = Account::new_ref(1, 0, &system_program::id()); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); // Stake some lamports (available lamports for withdrawals will reduce) let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); vote_keyed_account.set_state(&VoteState::default()).unwrap(); let signers = vec![stake_pubkey].into_iter().collect(); @@ -1789,9 +1801,11 @@ mod tests { let stake_history = create_stake_history_from_delegations( None, 0..future.epoch, - &[StakeState::stake_from(&stake_keyed_account.account) - .unwrap() - .delegation], + &[ + StakeState::stake_from(&stake_keyed_account.account.borrow()) + .unwrap() + .delegation, + ], ); // Try to withdraw stake @@ -1811,7 +1825,7 @@ mod tests { fn test_withdraw_stake_invalid_state() { let stake_pubkey = Pubkey::new_rand(); let total_lamports = 100; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( total_lamports, &StakeState::RewardsPool, std::mem::size_of::(), @@ -1820,7 +1834,7 @@ mod tests { .expect("stake_account"); let to = Pubkey::new_rand(); - let mut to_account = Account::new(1, 0, &system_program::id()); + let mut to_account = Account::new_ref(1, 0, &system_program::id()); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let signers = vec![stake_pubkey].into_iter().collect(); @@ -1841,7 +1855,7 @@ mod tests { let stake_pubkey = Pubkey::new_rand(); let custodian = Pubkey::new_rand(); let total_lamports = 100; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( total_lamports, &StakeState::Initialized(Meta { lockup: Lockup { @@ -1857,7 +1871,7 @@ mod tests { .expect("stake_account"); let to = Pubkey::new_rand(); - let mut to_account = Account::new(1, 0, &system_program::id()); + let mut to_account = Account::new_ref(1, 0, &system_program::id()); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); @@ -1893,7 +1907,7 @@ mod tests { ); } // reset balance - stake_keyed_account.account.lamports = total_lamports; + stake_keyed_account.account.borrow_mut().lamports = total_lamports; // lockup has expired let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); @@ -1999,7 +2013,7 @@ mod tests { rewards.validator_point_value = 100.0; let rewards_pool_pubkey = Pubkey::new_rand(); - let mut rewards_pool_account = Account::new_data( + let mut rewards_pool_account = Account::new_ref_data( std::u64::MAX, &StakeState::RewardsPool, &crate::rewards_pools::id(), @@ -2010,7 +2024,7 @@ mod tests { let stake_pubkey = Pubkey::default(); let stake_lamports = 100; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -2021,8 +2035,12 @@ mod tests { let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); // not delegated yet, deserialization fails @@ -2044,9 +2062,11 @@ mod tests { let stake_history = create_stake_history_from_delegations( Some(100), 0..10, - &[StakeState::stake_from(&stake_keyed_account.account) - .unwrap() - .delegation], + &[ + StakeState::stake_from(&stake_keyed_account.account.borrow()) + .unwrap() + .delegation, + ], ); // no credits to claim @@ -2071,10 +2091,14 @@ mod tests { Err(InstructionError::InvalidAccountData) ); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); - let mut vote_state = VoteState::from(&vote_account).unwrap(); + let mut vote_state = VoteState::from(&vote_account.borrow()).unwrap(); // split credits 3:1 between staker and voter vote_state.commission = 25; // put in some credits in epoch 0 for which we should have a non-zero stake @@ -2083,11 +2107,11 @@ mod tests { } vote_state.increment_credits(2); - vote_state.to(&mut vote_account).unwrap(); + vote_state.to(&mut vote_account.borrow_mut()).unwrap(); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); // some credits to claim, but rewards pool empty (shouldn't ever happen) - rewards_pool_keyed_account.account.lamports = 1; + rewards_pool_keyed_account.account.borrow_mut().lamports = 1; assert_eq!( stake_keyed_account.redeem_vote_credits( &mut vote_keyed_account, @@ -2097,11 +2121,11 @@ mod tests { ), Err(InstructionError::UnbalancedInstruction) ); - rewards_pool_keyed_account.account.lamports = std::u64::MAX; + rewards_pool_keyed_account.account.borrow_mut().lamports = std::u64::MAX; // finally! some credits to claim - let stake_account_balance = stake_keyed_account.account.lamports; - let vote_account_balance = vote_keyed_account.account.lamports; + let stake_account_balance = stake_keyed_account.account.borrow().lamports; + let vote_account_balance = vote_keyed_account.account.borrow().lamports; assert_eq!( stake_keyed_account.redeem_vote_credits( &mut vote_keyed_account, @@ -2111,8 +2135,8 @@ mod tests { ), Ok(()) ); - let staker_rewards = stake_keyed_account.account.lamports - stake_account_balance; - let voter_commission = vote_keyed_account.account.lamports - vote_account_balance; + let staker_rewards = stake_keyed_account.account.borrow().lamports - stake_account_balance; + let voter_commission = vote_keyed_account.account.borrow().lamports - vote_account_balance; assert!(voter_commission > 0); assert!(staker_rewards > 0); assert!( @@ -2120,8 +2144,11 @@ mod tests { "rewards should be split ~3:1" ); // verify rewards are added to stake - let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap(); - assert_eq!(stake.delegation.stake, stake_keyed_account.account.lamports); + let stake = StakeState::stake_from(&stake_keyed_account.account.borrow()).unwrap(); + assert_eq!( + stake.delegation.stake, + stake_keyed_account.account.borrow().lamports + ); let wrong_vote_pubkey = Pubkey::new_rand(); let mut wrong_vote_keyed_account = @@ -2143,7 +2170,7 @@ mod tests { fn test_authorize_uninit() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::default(), std::mem::size_of::(), @@ -2168,7 +2195,7 @@ mod tests { fn test_authorize_lockup() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -2177,7 +2204,7 @@ mod tests { .expect("stake_account"); let to = Pubkey::new_rand(); - let mut to_account = Account::new(1, 0, &system_program::id()); + let mut to_account = Account::new_ref(1, 0, &system_program::id()); let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let clock = sysvar::clock::Clock::default(); @@ -2204,7 +2231,7 @@ mod tests { Ok(()) ); if let StakeState::Initialized(Meta { authorized, .. }) = - StakeState::from(&stake_keyed_account.account).unwrap() + StakeState::from(&stake_keyed_account.account.borrow()).unwrap() { assert_eq!(authorized.staker, stake_pubkey0); assert_eq!(authorized.withdrawer, stake_pubkey0); @@ -2238,7 +2265,7 @@ mod tests { Ok(()) ); if let StakeState::Initialized(Meta { authorized, .. }) = - StakeState::from(&stake_keyed_account.account).unwrap() + StakeState::from(&stake_keyed_account.account.borrow()).unwrap() { assert_eq!(authorized.staker, stake_pubkey2); } @@ -2253,7 +2280,7 @@ mod tests { Ok(()) ); if let StakeState::Initialized(Meta { authorized, .. }) = - StakeState::from(&stake_keyed_account.account).unwrap() + StakeState::from(&stake_keyed_account.account.borrow()).unwrap() { assert_eq!(authorized.staker, stake_pubkey2); } @@ -2290,7 +2317,7 @@ mod tests { fn test_split_source_uninitialized() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2299,7 +2326,7 @@ mod tests { .expect("stake_account"); let split_stake_pubkey = Pubkey::new_rand(); - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2328,8 +2355,8 @@ mod tests { Ok(()) ); assert_eq!( - stake_keyed_account.account.lamports, - split_stake_keyed_account.account.lamports + stake_keyed_account.account.borrow().lamports, + split_stake_keyed_account.account.borrow().lamports ); } @@ -2337,7 +2364,7 @@ mod tests { fn test_split_split_not_uninitialized() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Stake(Meta::auto(&stake_pubkey), Stake::just_stake(stake_lamports)), std::mem::size_of::(), @@ -2346,7 +2373,7 @@ mod tests { .expect("stake_account"); let split_stake_pubkey = Pubkey::new_rand(); - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -2379,7 +2406,7 @@ mod tests { fn test_split_more_than_staked() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Stake( Meta::auto(&stake_pubkey), @@ -2391,7 +2418,7 @@ mod tests { .expect("stake_account"); let split_stake_pubkey = Pubkey::new_rand(); - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2431,7 +2458,7 @@ mod tests { Stake::just_stake(stake_lamports - rent_exempt_reserve), ), ] { - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, state, std::mem::size_of::(), @@ -2442,7 +2469,7 @@ mod tests { let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2474,7 +2501,7 @@ mod tests { ); // split account already has way enough lamports - split_stake_keyed_account.account.lamports = 1_000; + split_stake_keyed_account.account.borrow_mut().lamports = 1_000; assert_eq!( stake_keyed_account.split( stake_lamports - rent_exempt_reserve, @@ -2499,9 +2526,12 @@ mod tests { } )) ); - assert_eq!(stake_keyed_account.account.lamports, rent_exempt_reserve); assert_eq!( - split_stake_keyed_account.account.lamports, + stake_keyed_account.account.borrow().lamports, + rent_exempt_reserve + ); + assert_eq!( + split_stake_keyed_account.account.borrow().lamports, 1_000 + stake_lamports - rent_exempt_reserve ); } @@ -2521,7 +2551,7 @@ mod tests { StakeState::Initialized(Meta::auto(&stake_pubkey)), StakeState::Stake(Meta::auto(&stake_pubkey), Stake::just_stake(stake_lamports)), ] { - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2532,7 +2562,7 @@ mod tests { let mut split_stake_keyed_account = KeyedAccount::new(&split_stake_pubkey, true, &mut split_stake_account); - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, state, std::mem::size_of::(), @@ -2563,7 +2593,8 @@ mod tests { ); // no lamport leakage assert_eq!( - stake_keyed_account.account.lamports + split_stake_keyed_account.account.lamports, + stake_keyed_account.account.borrow().lamports + + split_stake_keyed_account.account.borrow().lamports, stake_lamports ); @@ -2604,7 +2635,7 @@ mod tests { } // reset - stake_keyed_account.account.lamports = stake_lamports; + stake_keyed_account.account.borrow_mut().lamports = stake_lamports; } } @@ -2631,7 +2662,7 @@ mod tests { Stake::just_stake(stake_lamports - rent_exempt_reserve), ), ] { - let mut split_stake_account = Account::new_data_with_space( + let mut split_stake_account = Account::new_ref_data_with_space( 0, &StakeState::Uninitialized, std::mem::size_of::(), @@ -2642,7 +2673,7 @@ mod tests { let mut split_stake_keyed_account = KeyedAccount::new(&split_stake_pubkey, true, &mut split_stake_account); - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, state, std::mem::size_of::(), @@ -2660,7 +2691,8 @@ mod tests { // no lamport leakage assert_eq!( - stake_keyed_account.account.lamports + split_stake_keyed_account.account.lamports, + stake_keyed_account.account.borrow().lamports + + split_stake_keyed_account.account.borrow().lamports, stake_lamports ); @@ -2701,7 +2733,7 @@ mod tests { } // reset - stake_keyed_account.account.lamports = stake_lamports; + stake_keyed_account.account.borrow_mut().lamports = stake_lamports; } } @@ -2792,7 +2824,7 @@ mod tests { fn test_authorize_delegated_stake() { let stake_pubkey = Pubkey::new_rand(); let stake_lamports = 42; - let mut stake_account = Account::new_data_with_space( + let mut stake_account = Account::new_ref_data_with_space( stake_lamports, &StakeState::Initialized(Meta::auto(&stake_pubkey)), std::mem::size_of::(), @@ -2803,8 +2835,12 @@ mod tests { let mut clock = Clock::default(); let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); + let mut vote_account = RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); @@ -2823,7 +2859,8 @@ mod tests { ), Ok(()) ); - let authorized = StakeState::authorized_from(&stake_keyed_account.account).unwrap(); + let authorized = + StakeState::authorized_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap(); assert_eq!(authorized.staker, new_staker_pubkey); let other_pubkey = Pubkey::new_rand(); @@ -2834,8 +2871,12 @@ mod tests { let new_voter_pubkey = Pubkey::new_rand(); let vote_state = VoteState::default(); - let mut new_vote_account = - vote_state::create_account(&new_voter_pubkey, &Pubkey::new_rand(), 0, 100); + let mut new_vote_account = RefCell::new(vote_state::create_account( + &new_voter_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )); let mut new_vote_keyed_account = KeyedAccount::new(&new_voter_pubkey, false, &mut new_vote_account); new_vote_keyed_account.set_state(&vote_state).unwrap(); @@ -2864,7 +2905,8 @@ mod tests { ), Ok(()) ); - let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap(); + let stake = + StakeState::stake_from(&stake_keyed_account.try_account_ref().unwrap()).unwrap(); assert_eq!(stake.delegation.voter_pubkey, new_voter_pubkey); // Test another staking action diff --git a/programs/storage/src/storage_contract.rs b/programs/storage/src/storage_contract.rs index 559788a342..24b01d26fc 100644 --- a/programs/storage/src/storage_contract.rs +++ b/programs/storage/src/storage_contract.rs @@ -421,13 +421,13 @@ fn check_redeemable( owner: &mut StorageAccount, ) -> Result<(), InstructionError> { let rewards = (credits.redeemable as f64 * storage_point_value) as u64; - if rewards_pool.account.lamports < rewards { + if rewards_pool.lamports()? < rewards { Err(InstructionError::CustomError( StorageError::RewardPoolDepleted as u32, )) } else { if rewards >= 1 { - rewards_pool.account.lamports -= rewards; + rewards_pool.try_account_ref_mut()?.lamports -= rewards; owner.account.lamports += rewards; //clear credits credits.redeemable = 0; @@ -503,7 +503,7 @@ fn count_valid_proofs( mod tests { use super::*; use crate::{id, rewards_pools}; - use std::collections::BTreeMap; + use std::{cell::RefCell, collections::BTreeMap}; #[test] fn test_account_data() { @@ -617,7 +617,7 @@ mod tests { lamports: 1, ..Account::default() }; - let mut rewards_pool = create_rewards_pool(); + let mut rewards_pool = RefCell::new(create_rewards_pool()); let pool_id = rewards_pools::id(); let mut keyed_pool_account = KeyedAccount::new(&pool_id, false, &mut rewards_pool); let mut owner = StorageAccount { @@ -626,7 +626,7 @@ mod tests { }; // check that redeeming from depleted pools fails - keyed_pool_account.account.lamports = 0; + keyed_pool_account.account.borrow_mut().lamports = 0; assert_eq!( check_redeemable(&mut credits, 1.0, &mut keyed_pool_account, &mut owner), Err(InstructionError::CustomError( @@ -635,7 +635,7 @@ mod tests { ); assert_eq!(owner.account.lamports, 1); - keyed_pool_account.account.lamports = 200; + keyed_pool_account.account.borrow_mut().lamports = 200; assert_eq!( check_redeemable(&mut credits, 1.0, &mut keyed_pool_account, &mut owner), Ok(()) diff --git a/programs/storage/src/storage_processor.rs b/programs/storage/src/storage_processor.rs index 0e20d13dc1..d00327b0c1 100644 --- a/programs/storage/src/storage_processor.rs +++ b/programs/storage/src/storage_processor.rs @@ -19,7 +19,8 @@ pub fn process_instruction( let (me, rest) = keyed_accounts.split_at_mut(1); let me_unsigned = me[0].signer_key().is_none(); - let mut storage_account = StorageAccount::new(*me[0].unsigned_key(), &mut me[0].account); + let mut me_account = me[0].try_account_ref_mut()?; + let mut storage_account = StorageAccount::new(*me[0].unsigned_key(), &mut me_account); match limited_deserialize(data)? { StorageInstruction::InitializeStorage { @@ -68,7 +69,8 @@ pub fn process_instruction( let rewards = Rewards::from_keyed_account(&rewards[0])?; let clock = Clock::from_keyed_account(&clock[0])?; - let mut owner = StorageAccount::new(*owner[0].unsigned_key(), &mut owner[0].account); + let mut owner_account = owner[0].try_account_ref_mut()?; + let mut owner = StorageAccount::new(*owner[0].unsigned_key(), &mut owner_account); storage_account.claim_storage_reward(&mut rewards_pools[0], clock, rewards, &mut owner) } @@ -84,12 +86,16 @@ pub fn process_instruction( } let me_id = storage_account.id; let clock = Clock::from_keyed_account(&clock[0])?; - let mut rest: Vec<_> = rest + let mut rest = rest + .iter() + .map(|keyed_account| Ok((keyed_account, keyed_account.try_account_ref_mut()?))) + .collect::, InstructionError>>()?; + let mut rest = rest .iter_mut() - .map(|keyed_account| { - StorageAccount::new(*keyed_account.unsigned_key(), &mut keyed_account.account) + .map(|(keyed_account, account_ref)| { + StorageAccount::new(*keyed_account.unsigned_key(), account_ref) }) - .collect(); + .collect::>(); storage_account.proof_validation(&me_id, clock, segment, proofs, &mut rest) } } @@ -117,15 +123,20 @@ mod tests { Sysvar, }, }; + use std::cell::RefCell; fn test_instruction( ix: &Instruction, program_accounts: &mut [Account], ) -> Result<(), InstructionError> { + let program_accounts: Vec<_> = program_accounts + .iter() + .map(|account| RefCell::new(account.clone())) + .collect(); let mut keyed_accounts: Vec<_> = ix .accounts .iter() - .zip(program_accounts.iter_mut()) + .zip(program_accounts.iter()) .map(|(account_meta, account)| { KeyedAccount::new(&account_meta.pubkey, account_meta.is_signer, account) }) @@ -175,7 +186,7 @@ mod tests { #[test] fn test_storage_tx() { let pubkey = Pubkey::new_rand(); - let mut accounts = [(pubkey, Account::default())]; + let mut accounts = [(&pubkey, &RefCell::new(Account::default()))]; let mut keyed_accounts = create_keyed_accounts(&mut accounts); assert!(process_instruction(&id(), &mut keyed_accounts, &[]).is_err()); } @@ -185,8 +196,8 @@ mod tests { let pubkey = Pubkey::new_rand(); let clock_id = clock::id(); let mut keyed_accounts = Vec::new(); - let mut user_account = Account::default(); - let mut clock_account = Clock::default().create_account(1); + let mut user_account = RefCell::new(Account::default()); + let mut clock_account = RefCell::new(Clock::default().create_account(1)); keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account)); keyed_accounts.push(KeyedAccount::new(&clock_id, false, &mut clock_account)); diff --git a/programs/vest/src/vest_processor.rs b/programs/vest/src/vest_processor.rs index 3830db88ca..77509f8457 100644 --- a/programs/vest/src/vest_processor.rs +++ b/programs/vest/src/vest_processor.rs @@ -12,12 +12,13 @@ use solana_sdk::{ instruction_processor_utils::{limited_deserialize, next_keyed_account}, pubkey::Pubkey, }; +use std::cell::RefMut; fn verify_date_account( keyed_account: &mut KeyedAccount, expected_pubkey: &Pubkey, ) -> Result, InstructionError> { - if keyed_account.account.owner != solana_config_program::id() { + if keyed_account.owner()? != solana_config_program::id() { return Err(InstructionError::IncorrectProgramId); } @@ -34,18 +35,18 @@ fn verify_date_account( fn verify_account<'a>( keyed_account: &'a mut KeyedAccount, expected_pubkey: &Pubkey, -) -> Result<&'a mut Account, InstructionError> { +) -> Result, InstructionError> { if keyed_account.unsigned_key() != expected_pubkey { return Err(VestError::Unauthorized.into()); } - Ok(keyed_account.account) + keyed_account.try_account_ref_mut() } fn verify_signed_account<'a>( keyed_account: &'a mut KeyedAccount, expected_pubkey: &Pubkey, -) -> Result<&'a mut Account, InstructionError> { +) -> Result, InstructionError> { if keyed_account.signer_key().is_none() { return Err(InstructionError::MissingRequiredSignature); } @@ -59,7 +60,7 @@ pub fn process_instruction( data: &[u8], ) -> Result<(), InstructionError> { let keyed_accounts_iter = &mut keyed_accounts.iter_mut(); - let contract_account = &mut next_keyed_account(keyed_accounts_iter)?.account; + let contract_account = &mut next_keyed_account(keyed_accounts_iter)?.try_account_ref_mut()?; let instruction = limited_deserialize(data)?; @@ -104,11 +105,11 @@ pub fn process_instruction( next_keyed_account(keyed_accounts_iter)?, &vest_state.date_pubkey, )?; - let payee_account = verify_account( + let mut payee_account = verify_account( next_keyed_account(keyed_accounts_iter)?, &vest_state.payee_pubkey, )?; - vest_state.redeem_tokens(contract_account, current_date, payee_account); + vest_state.redeem_tokens(contract_account, current_date, &mut payee_account); } VestInstruction::Terminate | VestInstruction::Renege(_) => { let lamports = if let VestInstruction::Renege(lamports) = instruction { @@ -121,12 +122,12 @@ pub fn process_instruction( &vest_state.terminator_pubkey, )?; let payee_keyed_account = keyed_accounts_iter.next(); - let payee_account = if let Some(payee_keyed_account) = payee_keyed_account { - &mut payee_keyed_account.account + let mut payee_account = if let Some(payee_keyed_account) = payee_keyed_account { + payee_keyed_account.try_account_ref_mut()? } else { terminator_account }; - vest_state.renege(contract_account, payee_account, lamports); + vest_state.renege(contract_account, &mut payee_account, lamports); } VestInstruction::VestAll => { verify_signed_account( @@ -264,7 +265,7 @@ mod tests { fn test_verify_account_unauthorized() { // Ensure client can't sneak in with an untrusted date account. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &solana_config_program::id()); + let mut account = Account::new_ref(1, 0, &solana_config_program::id()); let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); let mallory_pubkey = Pubkey::new_rand(); // <-- Attack! Not the expected account. @@ -278,7 +279,7 @@ mod tests { fn test_verify_signed_account_missing_signature() { // Ensure client can't sneak in with an unsigned account. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &solana_config_program::id()); + let mut account = Account::new_ref(1, 0, &solana_config_program::id()); let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); // <-- Attack! Unsigned transaction. assert_eq!( @@ -291,7 +292,7 @@ mod tests { fn test_verify_date_account_incorrect_program_id() { // Ensure client can't sneak in with a non-Config account. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &id()); // <-- Attack! Pass Vest account where Config account is expected. + let mut account = Account::new_ref(1, 0, &id()); // <-- Attack! Pass Vest account where Config account is expected. let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); assert_eq!( verify_date_account(&mut keyed_account, &date_pubkey).unwrap_err(), @@ -303,7 +304,7 @@ mod tests { fn test_verify_date_account_uninitialized_config() { // Ensure no panic when `get_config_data()` returns an error. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 0, &solana_config_program::id()); // <-- Attack! Zero space. + let mut account = Account::new_ref(1, 0, &solana_config_program::id()); // <-- Attack! Zero space. let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); assert_eq!( verify_date_account(&mut keyed_account, &date_pubkey).unwrap_err(), @@ -315,7 +316,7 @@ mod tests { fn test_verify_date_account_invalid_date_config() { // Ensure no panic when `deserialize::()` returns an error. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 1, &solana_config_program::id()); // Attack! 1 byte, enough to sneak by `get_config_data()`, but not DateConfig deserialize. + let mut account = Account::new_ref(1, 1, &solana_config_program::id()); // Attack! 1 byte, enough to sneak by `get_config_data()`, but not DateConfig deserialize. let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); assert_eq!( verify_date_account(&mut keyed_account, &date_pubkey).unwrap_err(), @@ -327,7 +328,7 @@ mod tests { fn test_verify_date_account_deserialize() { // Ensure no panic when `deserialize::()` returns an error. let date_pubkey = Pubkey::new_rand(); - let mut account = Account::new(1, 1, &solana_config_program::id()); // Attack! 1 byte, enough to sneak by `get_config_data()`, but not DateConfig deserialize. + let mut account = Account::new_ref(1, 1, &solana_config_program::id()); // Attack! 1 byte, enough to sneak by `get_config_data()`, but not DateConfig deserialize. let mut keyed_account = KeyedAccount::new(&date_pubkey, false, &mut account); assert_eq!( verify_date_account(&mut keyed_account, &date_pubkey).unwrap_err(), diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 21be410356..95c1d9accf 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -228,8 +228,8 @@ pub fn process_instruction( #[cfg(test)] mod tests { use super::*; - use solana_sdk::account::Account; - use solana_sdk::rent::Rent; + use solana_sdk::{account::Account, rent::Rent}; + use std::cell::RefCell; // these are for 100% coverage in this file #[test] @@ -245,7 +245,7 @@ mod tests { .accounts .iter() .map(|meta| { - if sysvar::clock::check_id(&meta.pubkey) { + RefCell::new(if sysvar::clock::check_id(&meta.pubkey) { Clock::default().create_account(1) } else if sysvar::slot_hashes::check_id(&meta.pubkey) { SlotHashes::default().create_account(1) @@ -253,12 +253,12 @@ mod tests { Rent::free().create_account(1) } else { Account::default() - } + }) }) .collect(); for _ in 0..instruction.accounts.len() { - accounts.push(Account::default()); + accounts.push(RefCell::new(Account::default())); } { let mut keyed_accounts: Vec<_> = instruction diff --git a/programs/vote/src/vote_state.rs b/programs/vote/src/vote_state.rs index 50950b6d81..3b680bd866 100644 --- a/programs/vote/src/vote_state.rs +++ b/programs/vote/src/vote_state.rs @@ -501,11 +501,11 @@ pub fn withdraw( verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; - if vote_account.account.lamports < lamports { + if vote_account.lamports()? < lamports { return Err(InstructionError::InsufficientFunds); } - vote_account.account.lamports -= lamports; - to_account.account.lamports += lamports; + vote_account.try_account_ref_mut()?.lamports -= lamports; + to_account.try_account_ref_mut()?.lamports += lamports; Ok(()) } @@ -585,6 +585,7 @@ mod tests { hash::hash, instruction_processor_utils::next_keyed_account, }; + use std::cell::RefCell; const MAX_RECENT_VOTES: usize = 16; @@ -605,7 +606,7 @@ mod tests { #[test] fn test_initialize_vote_account() { let vote_account_pubkey = Pubkey::new_rand(); - let mut vote_account = Account::new(100, VoteState::size_of(), &id()); + let mut vote_account = Account::new_ref(100, VoteState::size_of(), &id()); let node_pubkey = Pubkey::new_rand(); @@ -637,17 +638,22 @@ mod tests { assert_eq!(res, Err(InstructionError::AccountAlreadyInitialized)); } - fn create_test_account() -> (Pubkey, Account) { + fn create_test_account() -> (Pubkey, RefCell) { let vote_pubkey = Pubkey::new_rand(); ( vote_pubkey, - vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100), + RefCell::new(vote_state::create_account( + &vote_pubkey, + &Pubkey::new_rand(), + 0, + 100, + )), ) } fn simulate_process_vote( vote_pubkey: &Pubkey, - vote_account: &mut Account, + vote_account: &mut RefCell, vote: &Vote, slot_hashes: &[SlotHash], epoch: Epoch, @@ -664,13 +670,13 @@ mod tests { &vote.clone(), &signers, )?; - vote_account.state() + vote_account.borrow().state() } /// exercises all the keyed accounts stuff fn simulate_process_vote_unchecked( vote_pubkey: &Pubkey, - vote_account: &mut Account, + vote_account: &mut RefCell, vote: &Vote, ) -> Result { simulate_process_vote( @@ -698,7 +704,7 @@ mod tests { fn test_voter_registration() { let (vote_pubkey, vote_account) = create_test_account(); - let vote_state: VoteState = vote_account.state().unwrap(); + let vote_state: VoteState = vote_account.borrow().state().unwrap(); assert_eq!(vote_state.authorized_voter, vote_pubkey); assert!(vote_state.votes.is_empty()); } @@ -759,14 +765,14 @@ mod tests { let signers = get_signers(keyed_accounts); let res = update_node(&mut keyed_accounts[0], &node_pubkey, &signers); assert_eq!(res, Err(InstructionError::MissingRequiredSignature)); - let vote_state: VoteState = vote_account.state().unwrap(); + let vote_state: VoteState = vote_account.borrow().state().unwrap(); assert!(vote_state.node_pubkey != node_pubkey); let keyed_accounts = &mut [KeyedAccount::new(&vote_pubkey, true, &mut vote_account)]; let signers = get_signers(keyed_accounts); let res = update_node(&mut keyed_accounts[0], &node_pubkey, &signers); assert_eq!(res, Ok(())); - let vote_state: VoteState = vote_account.state().unwrap(); + let vote_state: VoteState = vote_account.borrow().state().unwrap(); assert_eq!(vote_state.node_pubkey, node_pubkey); } @@ -839,7 +845,7 @@ mod tests { assert_eq!(res, Ok(())); // verify authorized_voter_pubkey can authorize authorized_voter_pubkey ;) - let mut authorized_voter_account = Account::default(); + let mut authorized_voter_account = RefCell::new(Account::default()); let keyed_accounts = &mut [ KeyedAccount::new(&vote_pubkey, false, &mut vote_account), KeyedAccount::new( @@ -873,7 +879,7 @@ mod tests { assert_eq!(res, Ok(())); // verify authorized_withdrawer can authorize authorized_withdrawer ;) - let mut withdrawer_account = Account::default(); + let mut withdrawer_account = RefCell::new(Account::default()); let keyed_accounts = &mut [ KeyedAccount::new(&vote_pubkey, false, &mut vote_account), KeyedAccount::new(&authorized_withdrawer_pubkey, true, &mut withdrawer_account), @@ -902,7 +908,7 @@ mod tests { assert_eq!(res, Err(InstructionError::MissingRequiredSignature)); // signed by authorized voter - let mut authorized_voter_account = Account::default(); + let mut authorized_voter_account = RefCell::new(Account::default()); let keyed_accounts = &mut [ KeyedAccount::new(&vote_pubkey, false, &mut vote_account), KeyedAccount::new( @@ -926,7 +932,7 @@ mod tests { #[test] fn test_vote_without_initialization() { let vote_pubkey = Pubkey::new_rand(); - let mut vote_account = Account::new(100, VoteState::size_of(), &id()); + let mut vote_account = RefCell::new(Account::new(100, VoteState::size_of(), &id())); let res = simulate_process_vote_unchecked( &vote_pubkey, @@ -940,7 +946,7 @@ mod tests { fn test_vote_lockout() { let (_vote_pubkey, vote_account) = create_test_account(); - let mut vote_state: VoteState = vote_account.state().unwrap(); + let mut vote_state: VoteState = vote_account.borrow().state().unwrap(); for i in 0..(MAX_LOCKOUT_HISTORY + 1) { vote_state.process_slot_vote_unchecked((INITIAL_LOCKOUT as usize * i) as u64); @@ -1254,7 +1260,11 @@ mod tests { let res = withdraw( &mut keyed_accounts[0], 0, - &mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()), + &mut KeyedAccount::new( + &Pubkey::new_rand(), + false, + &mut RefCell::new(Account::default()), + ), &signers, ); assert_eq!(res, Err(InstructionError::MissingRequiredSignature)); @@ -1265,14 +1275,18 @@ mod tests { let res = withdraw( &mut keyed_accounts[0], 101, - &mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()), + &mut KeyedAccount::new( + &Pubkey::new_rand(), + false, + &mut RefCell::new(Account::default()), + ), &signers, ); assert_eq!(res, Err(InstructionError::InsufficientFunds)); // all good - let mut to_account = Account::default(); - let lamports = vote_account.lamports; + let mut to_account = RefCell::new(Account::default()); + let lamports = vote_account.borrow().lamports; let keyed_accounts = &mut [KeyedAccount::new(&vote_pubkey, true, &mut vote_account)]; let signers = get_signers(keyed_accounts); let res = withdraw( @@ -1282,11 +1296,11 @@ mod tests { &signers, ); assert_eq!(res, Ok(())); - assert_eq!(vote_account.lamports, 0); - assert_eq!(to_account.lamports, lamports); + assert_eq!(vote_account.borrow().lamports, 0); + assert_eq!(to_account.borrow().lamports, lamports); // reset balance, verify that authorized_withdrawer works - vote_account.lamports = lamports; + vote_account.borrow_mut().lamports = lamports; // authorize authorized_withdrawer let authorized_withdrawer_pubkey = Pubkey::new_rand(); @@ -1302,7 +1316,7 @@ mod tests { assert_eq!(res, Ok(())); // withdraw using authorized_withdrawer to authorized_withdrawer's account - let mut withdrawer_account = Account::default(); + let mut withdrawer_account = RefCell::new(Account::default()); let keyed_accounts = &mut [ KeyedAccount::new(&vote_pubkey, false, &mut vote_account), KeyedAccount::new(&authorized_withdrawer_pubkey, true, &mut withdrawer_account), @@ -1318,8 +1332,8 @@ mod tests { &signers, ); assert_eq!(res, Ok(())); - assert_eq!(vote_account.lamports, 0); - assert_eq!(withdrawer_account.lamports, lamports); + assert_eq!(vote_account.borrow().lamports, 0); + assert_eq!(withdrawer_account.borrow().lamports, lamports); } #[test] diff --git a/runtime/benches/accounts.rs b/runtime/benches/accounts.rs index 5ed513e7fc..be1a8c85fb 100644 --- a/runtime/benches/accounts.rs +++ b/runtime/benches/accounts.rs @@ -21,6 +21,14 @@ fn deposit_many(bank: &Bank, pubkeys: &mut Vec, num: usize) { } } +#[bench] +fn bench_has_duplicates(bencher: &mut Bencher) { + bencher.iter(|| { + let data = test::black_box([1, 2, 3]); + assert!(!Accounts::has_duplicates(&data)); + }) +} + #[bench] fn test_accounts_create(bencher: &mut Bencher) { let (genesis_config, _) = create_genesis_config(10_000); diff --git a/runtime/benches/message_processor.rs b/runtime/benches/message_processor.rs index 2858ee47f4..ba2e4470b0 100644 --- a/runtime/benches/message_processor.rs +++ b/runtime/benches/message_processor.rs @@ -7,14 +7,6 @@ use solana_runtime::message_processor::*; use solana_sdk::{account::Account, pubkey::Pubkey}; use test::Bencher; -#[bench] -fn bench_has_duplicates(bencher: &mut Bencher) { - bencher.iter(|| { - let data = test::black_box([1, 2, 3]); - assert!(!has_duplicates(&data)); - }) -} - #[bench] fn bench_verify_account_changes_data(bencher: &mut Bencher) { solana_logger::setup(); diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index b1c4008a00..13160172ab 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -5,7 +5,6 @@ use crate::accounts_index::AccountsIndex; use crate::append_vec::StoredAccount; use crate::bank::{HashAgeKind, TransactionProcessResult}; use crate::blockhash_queue::BlockhashQueue; -use crate::message_processor::has_duplicates; use crate::nonce_utils::prepare_if_nonce_account; use crate::rent_collector::RentCollector; use crate::system_instruction_processor::{get_system_account_kind, SystemAccountKind}; @@ -87,6 +86,19 @@ impl Accounts { .accounts_from_stream(stream, local_paths, append_vecs_path) } + /// Return true if the slice has any duplicate elements + pub fn has_duplicates(xs: &[T]) -> bool { + // Note: This is an O(n^2) algorithm, but requires no heap allocations. The benchmark + // `bench_has_duplicates` in benches/message_processor.rs shows that this implementation is + // ~50 times faster than using HashSet for very short slices. + for i in 1..xs.len() { + if xs[i..].contains(&xs[i - 1]) { + return true; + } + } + false + } + fn load_tx_accounts( &self, storage: &AccountStorage, @@ -103,7 +115,7 @@ impl Accounts { Err(TransactionError::MissingSignatureForFee) } else { // Check for unique account keys - if has_duplicates(&message.account_keys) { + if Self::has_duplicates(&message.account_keys) { error_counters.account_loaded_twice += 1; return Err(TransactionError::AccountLoadedTwice); } @@ -1634,4 +1646,10 @@ mod tests { 1 ); } + + #[test] + fn test_has_duplicates() { + assert!(!Accounts::has_duplicates(&[1, 2])); + assert!(Accounts::has_duplicates(&[1, 2, 1])); + } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 8aeb4ae1df..2d7a4cbf42 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -3,7 +3,7 @@ //! on behalf of the caller, and a low-level API for when they have //! already been signed and verified. use crate::{ - accounts::{Accounts, TransactionLoadResult}, + accounts::{Accounts, TransactionAccounts, TransactionLoadResult, TransactionLoaders}, accounts_db::{AccountStorageEntry, AccountsDBSerialize, AppendVecId, ErrorCounters}, blockhash_queue::BlockhashQueue, message_processor::{MessageProcessor, ProcessInstruction}, @@ -51,9 +51,11 @@ use solana_sdk::{ use solana_stake_program::stake_state::Delegation; use solana_vote_program::vote_state::VoteState; use std::{ + cell::RefCell, collections::HashMap, io::{BufReader, Cursor, Error as IOError, Read}, path::{Path, PathBuf}, + rc::Rc, sync::atomic::{AtomicBool, AtomicU64, Ordering}, sync::{Arc, RwLock, RwLockReadGuard}, }; @@ -64,6 +66,8 @@ pub const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; // 32 GiB pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; type BankStatusCache = StatusCache>; +type TransactionAccountRefCells = Vec>>; +type TransactionLoaderRefCells = Vec)>>; #[derive(Default)] pub struct BankRc { @@ -1167,6 +1171,48 @@ impl Bank { } } + /// Converts Accounts into RefCell, this involves moving + /// ownership by draining the source + #[allow(clippy::wrong_self_convention)] + fn into_refcells( + accounts: &mut TransactionAccounts, + loaders: &mut TransactionLoaders, + ) -> (TransactionAccountRefCells, TransactionLoaderRefCells) { + let account_refcells: Vec<_> = accounts + .drain(..) + .map(|account| Rc::new(RefCell::new(account))) + .collect(); + let loader_refcells: Vec> = loaders + .iter_mut() + .map(|v| { + v.drain(..) + .map(|(pubkey, account)| (pubkey, RefCell::new(account))) + .collect() + }) + .collect(); + (account_refcells, loader_refcells) + } + + /// Converts back from RefCell to Account, this involves moving + /// ownership by draining the sources + fn from_refcells( + accounts: &mut TransactionAccounts, + loaders: &mut TransactionLoaders, + mut account_refcells: TransactionAccountRefCells, + loader_refcells: TransactionLoaderRefCells, + ) { + account_refcells.drain(..).for_each(|account_refcell| { + accounts.push(Rc::try_unwrap(account_refcell).unwrap().into_inner()) + }); + loaders + .iter_mut() + .zip(loader_refcells) + .for_each(|(ls, mut lrcs)| { + lrcs.drain(..) + .for_each(|(pubkey, lrc)| ls.push((pubkey, lrc.into_inner()))) + }); + } + #[allow(clippy::type_complexity)] pub fn load_and_execute_transactions( &self, @@ -1222,9 +1268,18 @@ impl Bank { (Err(e), hash_age_kind) => (Err(e.clone()), hash_age_kind.clone()), (Ok((accounts, loaders, _rents)), hash_age_kind) => { signature_count += u64::from(tx.message().header.num_required_signatures); - let process_result = - self.message_processor - .process_message(tx.message(), loaders, accounts); + + let (mut account_refcells, mut loader_refcells) = + Self::into_refcells(accounts, loaders); + + let process_result = self.message_processor.process_message( + tx.message(), + &mut loader_refcells, + &mut account_refcells, + ); + + Self::from_refcells(accounts, loaders, account_refcells, loader_refcells); + if let Err(TransactionError::InstructionError(_, _)) = &process_result { error_counters.instruction_error += 1; } @@ -2285,8 +2340,8 @@ mod tests { if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockInstruction::Deduction => { - keyed_accounts[1].account.lamports += 1; - keyed_accounts[2].account.lamports -= 1; + keyed_accounts[1].account.borrow_mut().lamports += 1; + keyed_accounts[2].account.borrow_mut().lamports -= 1; Ok(()) } } @@ -2873,8 +2928,8 @@ mod tests { // set up stakes, vote, and storage accounts bank.store_account(&stake.0, &stake.1); - bank.store_account(&validator_id, &validator_account); - bank.store_account(&archiver_id, &archiver_account); + bank.store_account(&validator_id, &validator_account.borrow()); + bank.store_account(&archiver_id, &archiver_account.borrow()); // generate some rewards let mut vote_state = VoteState::from(&vote_account).unwrap(); @@ -5136,4 +5191,56 @@ mod tests { assert_eq!(transaction_balances_set.pre_balances[2], vec![9, 0, 1]); assert_eq!(transaction_balances_set.post_balances[2], vec![8, 0, 1]); } + + #[test] + fn test_transaction_with_duplicate_accounts_in_instruction() { + let (genesis_config, mint_keypair) = create_genesis_config(500); + let mut bank = Bank::new(&genesis_config); + + fn mock_process_instruction( + _program_id: &Pubkey, + keyed_accounts: &mut [KeyedAccount], + data: &[u8], + ) -> result::Result<(), InstructionError> { + let lamports = data[0] as u64; + { + let mut to_account = keyed_accounts[1].try_account_ref_mut()?; + let mut dup_account = keyed_accounts[2].try_account_ref_mut()?; + dup_account.lamports -= lamports; + to_account.lamports += lamports; + } + keyed_accounts[0].try_account_ref_mut()?.lamports -= lamports; + keyed_accounts[1].try_account_ref_mut()?.lamports += lamports; + Ok(()) + } + + let mock_program_id = Pubkey::new(&[2u8; 32]); + bank.add_instruction_processor(mock_program_id, mock_process_instruction); + + let from_pubkey = Pubkey::new_rand(); + let to_pubkey = Pubkey::new_rand(); + let dup_pubkey = from_pubkey.clone(); + let from_account = Account::new(100, 1, &mock_program_id); + let to_account = Account::new(0, 1, &mock_program_id); + bank.store_account(&from_pubkey, &from_account); + bank.store_account(&to_pubkey, &to_account); + + let account_metas = vec![ + AccountMeta::new(from_pubkey, false), + AccountMeta::new(to_pubkey, false), + AccountMeta::new(dup_pubkey, false), + ]; + let instruction = Instruction::new(mock_program_id, &10, account_metas); + let tx = Transaction::new_signed_with_payer( + vec![instruction], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + bank.last_blockhash(), + ); + + let result = bank.process_transaction(&tx); + assert_eq!(result, Ok(())); + assert_eq!(bank.get_balance(&from_pubkey), 80); + assert_eq!(bank.get_balance(&to_pubkey), 20); + } } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 4ced6abfd7..7941e8ba1f 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -9,7 +9,9 @@ use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; use solana_sdk::system_program; use solana_sdk::transaction::TransactionError; +use std::cell::RefCell; use std::collections::HashMap; +use std::rc::Rc; use std::sync::RwLock; #[cfg(unix)] @@ -17,44 +19,6 @@ use libloading::os::unix::*; #[cfg(windows)] use libloading::os::windows::*; -/// Return true if the slice has any duplicate elements -pub fn has_duplicates(xs: &[T]) -> bool { - // Note: This is an O(n^2) algorithm, but requires no heap allocations. The benchmark - // `bench_has_duplicates` in benches/message_processor.rs shows that this implementation is - // ~50 times faster than using HashSet for very short slices. - for i in 1..xs.len() { - if xs[i..].contains(&xs[i - 1]) { - return true; - } - } - false -} - -/// Get mut references to a subset of elements. -fn get_subset_unchecked_mut<'a, T>( - xs: &'a mut [T], - indexes: &[u8], -) -> Result, InstructionError> { - // Since the compiler doesn't know the indexes are unique, dereferencing - // multiple mut elements is assumed to be unsafe. If, however, all - // indexes are unique, it's perfectly safe. The returned elements will share - // the liftime of the input slice. - - // Make certain there are no duplicate indexes. If there are, return an error - // because we can't return multiple mut references to the same element. - if has_duplicates(indexes) { - return Err(InstructionError::DuplicateAccountIndex); - } - - Ok(indexes - .iter() - .map(|i| { - let ptr = &mut xs[*i as usize] as *mut T; - unsafe { &mut *ptr } - }) - .collect()) -} - // The relevant state of an account before an Instruction executes, used // to verify account integrity after the Instruction completes pub struct PreInstructionAccount { @@ -204,8 +168,8 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - executable_accounts: &mut [(Pubkey, Account)], - program_accounts: &mut [&mut Account], + executable_accounts: &mut [(Pubkey, RefCell)], + program_accounts: &mut [Rc>], ) -> Result<(), InstructionError> { let program_id = instruction.program_id(&message.account_keys); let mut keyed_accounts = create_keyed_readonly_accounts(executable_accounts); @@ -234,7 +198,7 @@ impl MessageProcessor { keyed_accounts.append(&mut keyed_accounts2); assert!( - keyed_accounts[0].account.executable, + keyed_accounts[0].try_account_ref()?.executable, "loader not executable" ); @@ -257,8 +221,38 @@ impl MessageProcessor { ) } - fn sum_account_lamports(accounts: &mut [&mut Account]) -> u128 { - accounts.iter().map(|a| u128::from(a.lamports)).sum() + pub fn verify_account_references( + executable_accounts: &mut [(Pubkey, RefCell)], + program_accounts: &mut [Rc>], + ) -> Result<(), InstructionError> { + for account in program_accounts.iter() { + account + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowOutstanding)?; + } + for (_, account) in executable_accounts.iter() { + account + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowOutstanding)?; + } + Ok(()) + } + + pub fn sum_account_lamports(accounts: &[Rc>]) -> u128 { + // Note: This is an O(n^2) algorithm, + // but performed on a very small slice and requires no heap allocations + accounts + .iter() + .enumerate() + .map(|(i, a)| { + for account in accounts.iter().skip(i + 1) { + if Rc::ptr_eq(a, account) { + return 0; // don't double count duplicates + } + } + u128::from(a.borrow().lamports) + }) + .sum() } /// Execute an instruction @@ -269,8 +263,8 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - executable_accounts: &mut [(Pubkey, Account)], - program_accounts: &mut [&mut Account], + executable_accounts: &mut [(Pubkey, RefCell)], + program_accounts: &mut [Rc>], ) -> Result<(), InstructionError> { assert_eq!(instruction.accounts.len(), program_accounts.len()); let program_id = instruction.program_id(&message.account_keys); @@ -280,8 +274,9 @@ impl MessageProcessor { .enumerate() .map(|(i, account)| { let is_writable = message.is_writable(instruction.accounts[i] as usize); + let account = account.borrow(); PreInstructionAccount::new( - account, + &account, is_writable, need_account_data_checked(&account.owner, program_id, is_writable), ) @@ -292,11 +287,16 @@ impl MessageProcessor { self.process_instruction(message, instruction, executable_accounts, program_accounts)?; + // Verify all accounts have zero outstanding refs + Self::verify_account_references(executable_accounts, program_accounts)?; // Verify the instruction for (pre_account, post_account) in pre_accounts.iter().zip(program_accounts.iter()) { - verify_account_changes(&program_id, pre_account, post_account)?; + let post_account = post_account + .try_borrow() + .map_err(|_| InstructionError::AccountBorrowFailed)?; + verify_account_changes(&program_id, pre_account, &post_account)?; } - // The total sum of all the lamports in all the accounts cannot change. + // Verify total sum of all the lamports did not change let post_total = Self::sum_account_lamports(program_accounts); if pre_total != post_total { return Err(InstructionError::UnbalancedInstruction); @@ -310,19 +310,24 @@ impl MessageProcessor { pub fn process_message( &self, message: &Message, - loaders: &mut [Vec<(Pubkey, Account)>], - accounts: &mut [Account], + loaders: &mut [Vec<(Pubkey, RefCell)>], + accounts: &mut [Rc>], ) -> Result<(), TransactionError> { for (instruction_index, instruction) in message.instructions.iter().enumerate() { let executable_index = message .program_position(instruction.program_id_index as usize) .ok_or(TransactionError::InvalidAccountIndex)?; let executable_accounts = &mut loaders[executable_index]; - let mut program_accounts = get_subset_unchecked_mut(accounts, &instruction.accounts) - .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?; - // TODO: `get_subset_unchecked_mut` panics on an index out of bounds if an executable + + // TODO: panics on an index out of bounds if an executable // account is also included as a regular account for an instruction, because the // executable account is not passed in as part of the accounts slice + let mut program_accounts: Vec<_> = instruction + .accounts + .iter() + .map(|i| accounts[*i as usize].clone()) + .collect(); + self.execute_instruction( message, instruction, @@ -373,37 +378,72 @@ mod tests { } #[test] - fn test_has_duplicates() { - assert!(!has_duplicates(&[1, 2])); - assert!(has_duplicates(&[1, 2, 1])); + fn test_verify_account_references() { + let mut executable_accounts = vec![(Pubkey::new_rand(), RefCell::new(Account::default()))]; + let mut program_accounts = vec![Rc::new(RefCell::new(Account::default()))]; + + assert!(MessageProcessor::verify_account_references( + &mut executable_accounts, + &mut program_accounts, + ) + .is_ok()); + + let cloned = program_accounts[0].clone(); + let _borrowed = cloned.borrow(); + assert_eq!( + MessageProcessor::verify_account_references( + &mut executable_accounts, + &mut program_accounts, + ), + Err(InstructionError::AccountBorrowOutstanding) + ); + + // TODO when the `&mut`s go away test outstanding executable_account refs } #[test] - fn test_get_subset_unchecked_mut() { - assert_eq!( - get_subset_unchecked_mut(&mut [7, 8], &[0]).unwrap(), - vec![&mut 7] - ); - assert_eq!( - get_subset_unchecked_mut(&mut [7, 8], &[0, 1]).unwrap(), - vec![&mut 7, &mut 8] - ); - } + fn test_sum_account_lamports() { + let owner_pubkey = Pubkey::new_rand(); + let account1 = Rc::new(RefCell::new(Account::new(1, 1, &owner_pubkey))); + let account2 = Rc::new(RefCell::new(Account::new(2, 1, &owner_pubkey))); + let account3 = Rc::new(RefCell::new(Account::new(3, 1, &owner_pubkey))); - #[test] - fn test_get_subset_unchecked_mut_duplicate_index() { - // This panics, because it assumes duplicate detection is done elsewhere. + assert_eq!(0, MessageProcessor::sum_account_lamports(&mut vec![])); assert_eq!( - get_subset_unchecked_mut(&mut [7, 8], &[0, 0]).unwrap_err(), - InstructionError::DuplicateAccountIndex + 6, + MessageProcessor::sum_account_lamports(&mut vec![ + account1.clone(), + account2.clone(), + account3.clone() + ]) + ); + assert_eq!( + 3, + MessageProcessor::sum_account_lamports(&mut vec![ + account1.clone(), + account2.clone(), + account1.clone() + ]) + ); + assert_eq!( + 1, + MessageProcessor::sum_account_lamports(&mut vec![ + account1.clone(), + account1.clone(), + account1.clone() + ]) + ); + assert_eq!( + 6, + MessageProcessor::sum_account_lamports(&mut vec![ + account1.clone(), + account2.clone(), + account3.clone(), + account1.clone(), + account2.clone(), + account3.clone(), + ]) ); - } - - #[test] - #[should_panic] - fn test_get_subset_unchecked_mut_out_of_bounds() { - // This panics, because it assumes bounds validation is done elsewhere. - get_subset_unchecked_mut(&mut [7, 8], &[2]).unwrap(); } #[test] @@ -772,13 +812,13 @@ mod tests { match instruction { MockSystemInstruction::Correct => Ok(()), MockSystemInstruction::AttemptCredit { lamports } => { - keyed_accounts[0].account.lamports -= lamports; - keyed_accounts[1].account.lamports += lamports; + keyed_accounts[0].account.borrow_mut().lamports -= lamports; + keyed_accounts[1].account.borrow_mut().lamports += lamports; Ok(()) } // Change data in a read-only account MockSystemInstruction::AttemptDataChange { data } => { - keyed_accounts[1].account.data = vec![data]; + keyed_accounts[1].account.borrow_mut().data = vec![data]; Ok(()) } } @@ -792,14 +832,14 @@ mod tests { message_processor .add_instruction_processor(mock_system_program_id, mock_system_process_instruction); - let mut accounts: Vec = Vec::new(); - let account = Account::new(100, 1, &mock_system_program_id); + let mut accounts: Vec>> = Vec::new(); + let account = Account::new_ref(100, 1, &mock_system_program_id); accounts.push(account); - let account = Account::new(0, 1, &mock_system_program_id); + let account = Account::new_ref(0, 1, &mock_system_program_id); accounts.push(account); - let mut loaders: Vec> = Vec::new(); - let account = create_loadable_account("mock_system_program"); + let mut loaders: Vec)>> = Vec::new(); + let account = RefCell::new(create_loadable_account("mock_system_program")); loaders.push(vec![(mock_system_program_id, account)]); let from_pubkey = Pubkey::new_rand(); @@ -816,8 +856,8 @@ mod tests { let result = message_processor.process_message(&message, &mut loaders, &mut accounts); assert_eq!(result, Ok(())); - assert_eq!(accounts[0].lamports, 100); - assert_eq!(accounts[1].lamports, 0); + assert_eq!(accounts[0].borrow().lamports, 100); + assert_eq!(accounts[1].borrow().lamports, 0); let message = Message::new(vec![Instruction::new( mock_system_program_id, @@ -849,4 +889,124 @@ mod tests { )) ); } + + #[test] + fn test_process_message_duplicate_accounts() { + #[derive(Serialize, Deserialize)] + enum MockSystemInstruction { + BorrowFail, + MultiBorrowMut, + DoWork { lamports: u64, data: u8 }, + } + + fn mock_system_process_instruction( + _program_id: &Pubkey, + keyed_accounts: &mut [KeyedAccount], + data: &[u8], + ) -> Result<(), InstructionError> { + if let Ok(instruction) = bincode::deserialize(data) { + match instruction { + MockSystemInstruction::BorrowFail => { + let from_account = keyed_accounts[0].try_account_ref_mut()?; + let dup_account = keyed_accounts[2].try_account_ref_mut()?; + if from_account.lamports != dup_account.lamports { + return Err(InstructionError::InvalidArgument); + } + Ok(()) + } + MockSystemInstruction::MultiBorrowMut => { + let from_lamports = { + let from_account = keyed_accounts[0].try_account_ref_mut()?; + from_account.lamports + }; + let dup_lamports = { + let dup_account = keyed_accounts[2].try_account_ref_mut()?; + dup_account.lamports + }; + if from_lamports != dup_lamports { + return Err(InstructionError::InvalidArgument); + } + Ok(()) + } + MockSystemInstruction::DoWork { lamports, data } => { + { + let mut to_account = keyed_accounts[1].try_account_ref_mut()?; + let mut dup_account = keyed_accounts[2].try_account_ref_mut()?; + dup_account.lamports -= lamports; + to_account.lamports += lamports; + dup_account.data = vec![data]; + } + keyed_accounts[0].try_account_ref_mut()?.lamports -= lamports; + keyed_accounts[1].try_account_ref_mut()?.lamports += lamports; + Ok(()) + } + } + } else { + Err(InstructionError::InvalidInstructionData) + } + } + + let mock_program_id = Pubkey::new(&[2u8; 32]); + let mut message_processor = MessageProcessor::default(); + message_processor + .add_instruction_processor(mock_program_id, mock_system_process_instruction); + + let mut accounts: Vec>> = Vec::new(); + let account = Account::new_ref(100, 1, &mock_program_id); + accounts.push(account); + let account = Account::new_ref(0, 1, &mock_program_id); + accounts.push(account); + + let mut loaders: Vec)>> = Vec::new(); + let account = RefCell::new(create_loadable_account("mock_system_program")); + loaders.push(vec![(mock_program_id, account)]); + + let from_pubkey = Pubkey::new_rand(); + let to_pubkey = Pubkey::new_rand(); + let dup_pubkey = from_pubkey.clone(); + let account_metas = vec![ + AccountMeta::new(from_pubkey, true), + AccountMeta::new(to_pubkey, false), + AccountMeta::new(dup_pubkey, false), + ]; + + // Try to borrow mut the same account + let message = Message::new(vec![Instruction::new( + mock_program_id, + &MockSystemInstruction::BorrowFail, + account_metas.clone(), + )]); + let result = message_processor.process_message(&message, &mut loaders, &mut accounts); + assert_eq!( + result, + Err(TransactionError::InstructionError( + 0, + InstructionError::AccountBorrowFailed + )) + ); + + // Try to borrow mut the same account in a safe way + let message = Message::new(vec![Instruction::new( + mock_program_id, + &MockSystemInstruction::MultiBorrowMut, + account_metas.clone(), + )]); + let result = message_processor.process_message(&message, &mut loaders, &mut accounts); + assert_eq!(result, Ok(())); + + // Do work on the same account but at different location in keyed_accounts[] + let message = Message::new(vec![Instruction::new( + mock_program_id, + &MockSystemInstruction::DoWork { + lamports: 10, + data: 42, + }, + account_metas, + )]); + let result = message_processor.process_message(&message, &mut loaders, &mut accounts); + assert_eq!(result, Ok(())); + assert_eq!(accounts[0].borrow().lamports, 80); + assert_eq!(accounts[1].borrow().lamports, 20); + assert_eq!(accounts[0].borrow().data, vec![42]); + } } diff --git a/runtime/src/native_loader.rs b/runtime/src/native_loader.rs index 209a94aa77..150cdc85b0 100644 --- a/runtime/src/native_loader.rs +++ b/runtime/src/native_loader.rs @@ -72,7 +72,7 @@ pub fn invoke_entrypoint( ) -> Result<(), InstructionError> { // dispatch it let (names, params) = keyed_accounts.split_at_mut(1); - let name_vec = &names[0].account.data; + let name_vec = &names[0].try_account_ref()?.data; if let Some(entrypoint) = symbol_cache.read().unwrap().get(name_vec) { unsafe { return entrypoint(program_id, params, instruction_data); diff --git a/runtime/src/nonce_utils.rs b/runtime/src/nonce_utils.rs index 31fb02d6cc..8fb9850e08 100644 --- a/runtime/src/nonce_utils.rs +++ b/runtime/src/nonce_utils.rs @@ -196,14 +196,20 @@ mod tests { nonce_account .nonce_initialize(&authorized, &recent_blockhashes, &Rent::free()) .unwrap(); - assert!(verify_nonce(&nonce_account.account, &recent_blockhashes[0])); + assert!(verify_nonce( + &nonce_account.account.borrow(), + &recent_blockhashes[0] + )); }); } #[test] fn verify_nonce_bad_acc_state_fail() { with_test_keyed_account(42, true, |nonce_account| { - assert!(!verify_nonce(&nonce_account.account, &Hash::default())); + assert!(!verify_nonce( + &nonce_account.account.borrow(), + &Hash::default() + )); }); } @@ -221,7 +227,7 @@ mod tests { .nonce_initialize(&authorized, &recent_blockhashes, &Rent::free()) .unwrap(); assert!(!verify_nonce( - &nonce_account.account, + &nonce_account.account.borrow(), &recent_blockhashes[1] )); }); diff --git a/runtime/src/storage_utils.rs b/runtime/src/storage_utils.rs index cc19170f41..d57ce5ca35 100644 --- a/runtime/src/storage_utils.rs +++ b/runtime/src/storage_utils.rs @@ -91,7 +91,7 @@ pub(crate) mod tests { storage_instruction::{self, StorageAccountType}, storage_processor, }; - use std::sync::Arc; + use std::{cell::RefCell, rc::Rc, sync::Arc}; #[test] fn test_store_and_recover() { @@ -149,25 +149,24 @@ pub(crate) mod tests { let ((validator_pubkey, validator_account), (archiver_pubkey, archiver_account)) = create_storage_accounts_with_credits(credits); - storage_accounts.store(&validator_pubkey, &validator_account); - storage_accounts.store(&archiver_pubkey, &archiver_account); + storage_accounts.store(&validator_pubkey, &validator_account.borrow()); + storage_accounts.store(&archiver_pubkey, &archiver_account.borrow()); // check that 2x credits worth of points are available assert_eq!(storage_accounts.points(), credits * 2); - - let ((validator_pubkey, validator_account), (archiver_pubkey, mut archiver_account)) = + let ((validator_pubkey, validator_account), (archiver_pubkey, archiver_account)) = create_storage_accounts_with_credits(credits); - storage_accounts.store(&validator_pubkey, &validator_account); - storage_accounts.store(&archiver_pubkey, &archiver_account); + storage_accounts.store(&validator_pubkey, &validator_account.borrow()); + storage_accounts.store(&archiver_pubkey, &archiver_account.borrow()); // check that 4x credits worth of points are available assert_eq!(storage_accounts.points(), credits * 2 * 2); - storage_accounts.store(&validator_pubkey, &validator_account); - storage_accounts.store(&archiver_pubkey, &archiver_account); + storage_accounts.store(&validator_pubkey, &validator_account.borrow()); + storage_accounts.store(&archiver_pubkey, &archiver_account.borrow()); // check that storing again has no effect assert_eq!(storage_accounts.points(), credits * 2 * 2); - let storage_contract = &mut archiver_account.state().unwrap(); + let storage_contract = &mut archiver_account.borrow().state().unwrap(); if let StorageContract::ArchiverStorage { credits: account_credits, .. @@ -175,8 +174,11 @@ pub(crate) mod tests { { account_credits.current_epoch += 1; } - archiver_account.set_state(storage_contract).unwrap(); - storage_accounts.store(&archiver_pubkey, &archiver_account); + archiver_account + .borrow_mut() + .set_state(storage_contract) + .unwrap(); + storage_accounts.store(&archiver_pubkey, &archiver_account.borrow()); // check that incremental store increases credits assert_eq!(storage_accounts.points(), credits * 2 * 2 + 1); @@ -188,48 +190,55 @@ pub(crate) mod tests { pub fn create_storage_accounts_with_credits( credits: u64, - ) -> ((Pubkey, Account), (Pubkey, Account)) { + ) -> ( + (Pubkey, Rc>), + (Pubkey, Rc>), + ) { let validator_pubkey = Pubkey::new_rand(); let archiver_pubkey = Pubkey::new_rand(); - - let mut validator_account = Account::new( + let validator_account = Account::new_ref( 1, STORAGE_ACCOUNT_SPACE as usize, &solana_storage_program::id(), ); - let mut validator = StorageAccount::new(validator_pubkey, &mut validator_account); - validator - .initialize_storage(validator_pubkey, StorageAccountType::Validator) - .unwrap(); - let storage_contract = &mut validator_account.state().unwrap(); - if let StorageContract::ValidatorStorage { - credits: account_credits, - .. - } = storage_contract - { - account_credits.current_epoch = credits; - } - validator_account.set_state(storage_contract).unwrap(); - - let mut archiver_account = Account::new( + let archiver_account = Account::new_ref( 1, STORAGE_ACCOUNT_SPACE as usize, &solana_storage_program::id(), ); - let mut archiver = StorageAccount::new(archiver_pubkey, &mut archiver_account); - archiver - .initialize_storage(archiver_pubkey, StorageAccountType::Archiver) - .unwrap(); - let storage_contract = &mut archiver_account.state().unwrap(); - if let StorageContract::ArchiverStorage { - credits: account_credits, - .. - } = storage_contract { - account_credits.current_epoch = credits; - } - archiver_account.set_state(storage_contract).unwrap(); + StorageAccount::new(validator_pubkey, &mut validator_account.borrow_mut()) + .initialize_storage(validator_pubkey, StorageAccountType::Validator) + .unwrap(); + let storage_contract = &mut validator_account.borrow().state().unwrap(); + if let StorageContract::ValidatorStorage { + credits: account_credits, + .. + } = storage_contract + { + account_credits.current_epoch = credits; + } + validator_account + .borrow_mut() + .set_state(storage_contract) + .unwrap(); + StorageAccount::new(archiver_pubkey, &mut archiver_account.borrow_mut()) + .initialize_storage(archiver_pubkey, StorageAccountType::Archiver) + .unwrap(); + let storage_contract = &mut archiver_account.borrow().state().unwrap(); + if let StorageContract::ArchiverStorage { + credits: account_credits, + .. + } = storage_contract + { + account_credits.current_epoch = credits; + } + archiver_account + .borrow_mut() + .set_state(storage_contract) + .unwrap(); + } ( (validator_pubkey, validator_account), (archiver_pubkey, archiver_account), diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index 2b20a6e8e3..6a3d10dd88 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -151,20 +151,20 @@ fn transfer( return Err(InstructionError::MissingRequiredSignature); } - if !from.account.data.is_empty() { + if !from.data_is_empty()? { debug!("Transfer: `from` must not carry data"); return Err(InstructionError::InvalidArgument); } - - if lamports > from.account.lamports { + if lamports > from.lamports()? { debug!( "Transfer: insufficient lamports ({}, need {})", - from.account.lamports, lamports + from.lamports()?, + lamports ); return Err(SystemError::ResultWithNegativeLamports.into()); } - from.account.lamports -= lamports; + from.try_account_ref_mut()?.lamports -= lamports; to.lamports += lamports; Ok(()) } @@ -190,10 +190,11 @@ pub fn process_instruction( } => { let from = next_keyed_account(keyed_accounts_iter)?; let to = next_keyed_account(keyed_accounts_iter)?; + let mut to_account = to.try_account_ref_mut()?; let to_address = Address::create(to.unsigned_key(), None)?; create_account( from, - to.account, + &mut to_account, &to_address, lamports, space, @@ -210,12 +211,12 @@ pub fn process_instruction( } => { let from = next_keyed_account(keyed_accounts_iter)?; let to = next_keyed_account(keyed_accounts_iter)?; + let mut to_account = to.try_account_ref_mut()?; let to_address = Address::create(&to.unsigned_key(), Some((&base, &seed, &program_id)))?; - create_account( from, - to.account, + &mut to_account, &to_address, lamports, space, @@ -225,13 +226,14 @@ pub fn process_instruction( } SystemInstruction::Assign { program_id } => { let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create(keyed_account.unsigned_key(), None)?; - assign(keyed_account.account, &address, &program_id, &signers) + assign(&mut account, &address, &program_id, &signers) } SystemInstruction::Transfer { lamports } => { let from = next_keyed_account(keyed_accounts_iter)?; - let to = next_keyed_account(keyed_accounts_iter)?; - transfer(from, to.account, lamports) + let mut to_account = next_keyed_account(keyed_accounts_iter)?.try_account_ref_mut()?; + transfer(from, &mut to_account, lamports) } SystemInstruction::AdvanceNonceAccount => { let me = &mut next_keyed_account(keyed_accounts_iter)?; @@ -265,9 +267,9 @@ pub fn process_instruction( } SystemInstruction::Allocate { space } => { let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create(keyed_account.unsigned_key(), None)?; - - allocate(keyed_account.account, &address, space, &signers) + allocate(&mut account, &address, space, &signers) } SystemInstruction::AllocateWithSeed { base, @@ -276,17 +278,12 @@ pub fn process_instruction( program_id, } => { let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), Some((&base, &seed, &program_id)), )?; - allocate_and_assign( - keyed_account.account, - &address, - space, - &program_id, - &signers, - ) + allocate_and_assign(&mut account, &address, space, &program_id, &signers) } SystemInstruction::AssignWithSeed { base, @@ -294,12 +291,13 @@ pub fn process_instruction( program_id, } => { let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), Some((&base, &seed, &program_id)), )?; - assign(keyed_account.account, &address, &program_id, &signers) + assign(&mut account, &address, &program_id, &signers) } } } @@ -341,6 +339,7 @@ mod tests { system_instruction, system_program, sysvar, transaction::TransactionError, }; + use std::cell::RefCell; impl From for Address { fn from(address: Pubkey) -> Self { @@ -351,13 +350,29 @@ mod tests { } } + fn create_default_account() -> RefCell { + RefCell::new(Account::default()) + } + fn create_default_recent_blockhashes_account() -> RefCell { + RefCell::new(sysvar::recent_blockhashes::create_account_with_data( + 1, + vec![(0u64, &Hash::default()); 32].into_iter(), + )) + } + fn create_default_rent_account() -> RefCell { + RefCell::new(sysvar::recent_blockhashes::create_account_with_data( + 1, + vec![(0u64, &Hash::default()); 32].into_iter(), + )) + } + #[test] fn test_create_account() { let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); let to = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); - let mut to_account = Account::new(0, 0, &Pubkey::default()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); + let mut to_account = Account::new_ref(0, 0, &Pubkey::default()); assert_eq!( process_instruction( @@ -375,10 +390,10 @@ mod tests { ), Ok(()) ); - assert_eq!(from_account.lamports, 50); - assert_eq!(to_account.lamports, 50); - assert_eq!(to_account.owner, new_program_owner); - assert_eq!(to_account.data, [0, 0]); + assert_eq!(from_account.borrow().lamports, 50); + assert_eq!(to_account.borrow().lamports, 50); + assert_eq!(to_account.borrow().owner, new_program_owner); + assert_eq!(to_account.borrow().data, [0, 0]); } #[test] @@ -388,8 +403,8 @@ mod tests { let seed = "shiny pepper"; let to = create_address_with_seed(&from, seed, &new_program_owner).unwrap(); - let mut from_account = Account::new(100, 0, &system_program::id()); - let mut to_account = Account::new(0, 0, &Pubkey::default()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); + let mut to_account = Account::new_ref(0, 0, &Pubkey::default()); assert_eq!( process_instruction( @@ -409,10 +424,10 @@ mod tests { ), Ok(()) ); - assert_eq!(from_account.lamports, 50); - assert_eq!(to_account.lamports, 50); - assert_eq!(to_account.owner, new_program_owner); - assert_eq!(to_account.data, [0, 0]); + assert_eq!(from_account.borrow().lamports, 50); + assert_eq!(to_account.borrow().lamports, 50); + assert_eq!(to_account.borrow().owner, new_program_owner); + assert_eq!(to_account.borrow().data, [0, 0]); } #[test] @@ -435,13 +450,13 @@ mod tests { let seed = "dull boy"; let to = create_address_with_seed(&from, seed, &new_program_owner).unwrap(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let from_account = Account::new_ref(100, 0, &system_program::id()); let mut to_account = Account::new(0, 0, &Pubkey::default()); let to_address = Address::create(&to, Some((&from, seed, &new_program_owner))).unwrap(); assert_eq!( create_account( - &mut KeyedAccount::new(&from, false, &mut from_account), + &mut KeyedAccount::new(&from, false, &from_account), &mut to_account, &to_address, 50, @@ -451,7 +466,7 @@ mod tests { ), Err(InstructionError::MissingRequiredSignature) ); - assert_eq!(from_account.lamports, 100); + assert_eq!(from_account.borrow().lamports, 100); assert_eq!(to_account, Account::default()); } @@ -460,14 +475,14 @@ mod tests { // create account with zero lamports tranferred let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 1, &Pubkey::new_rand()); // not from system account + let from_account = Account::new_ref(100, 1, &Pubkey::new_rand()); // not from system account let to = Pubkey::new_rand(); let mut to_account = Account::new(0, 0, &Pubkey::default()); assert_eq!( create_account( - &mut KeyedAccount::new(&from, false, &mut from_account), // no signer + &mut KeyedAccount::new(&from, false, &from_account), // no signer &mut to_account, &to.into(), 0, @@ -478,7 +493,7 @@ mod tests { Ok(()) ); - let from_lamports = from_account.lamports; + let from_lamports = from_account.borrow().lamports; let to_lamports = to_account.lamports; let to_owner = to_account.owner; let to_data = to_account.data.clone(); @@ -493,7 +508,7 @@ mod tests { // Attempt to create account with more lamports than remaining in from_account let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); let to = Pubkey::new_rand(); let mut to_account = Account::new(0, 0, &Pubkey::default()); @@ -512,7 +527,7 @@ mod tests { #[test] fn test_request_more_than_allowed_data_length() { - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); let from = Pubkey::new_rand(); let mut to_account = Account::default(); let to = Pubkey::new_rand(); @@ -556,7 +571,7 @@ mod tests { // Attempt to create system account in account already owned by another program let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let from_account = Account::new_ref(100, 0, &system_program::id()); let original_program_owner = Pubkey::new(&[5; 32]); let owned_key = Pubkey::new_rand(); @@ -567,7 +582,7 @@ mod tests { let owned_address = owned_key.into(); let result = create_account( - &mut KeyedAccount::new(&from, true, &mut from_account), + &mut KeyedAccount::new(&from, true, &from_account), &mut owned_account, &owned_address, 50, @@ -577,7 +592,7 @@ mod tests { ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); - let from_lamports = from_account.lamports; + let from_lamports = from_account.borrow().lamports; assert_eq!(from_lamports, 100); assert_eq!(owned_account, unchanged_account); @@ -585,7 +600,7 @@ mod tests { let mut owned_account = Account::new(0, 1, &Pubkey::default()); let unchanged_account = owned_account.clone(); let result = create_account( - &mut KeyedAccount::new(&from, true, &mut from_account), + &mut KeyedAccount::new(&from, true, &from_account), &mut owned_account, &owned_address, 50, @@ -594,14 +609,14 @@ mod tests { &signers, ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); - let from_lamports = from_account.lamports; + let from_lamports = from_account.borrow().lamports; assert_eq!(from_lamports, 100); assert_eq!(owned_account, unchanged_account); // Verify that create_account works even if `to` has a non-zero balance let mut owned_account = Account::new(1, 0, &Pubkey::default()); let result = create_account( - &mut KeyedAccount::new(&from, true, &mut from_account), + &mut KeyedAccount::new(&from, true, &from_account), &mut owned_account, &owned_address, 50, @@ -610,7 +625,7 @@ mod tests { &signers, ); assert_eq!(result, Ok(())); - assert_eq!(from_account.lamports, from_lamports - 50); + assert_eq!(from_account.borrow().lamports, from_lamports - 50); assert_eq!(owned_account.lamports, 1 + 50); } @@ -619,7 +634,7 @@ mod tests { // Attempt to create an account without signing the transfer let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); let owned_key = Pubkey::new_rand(); let mut owned_account = Account::new(0, 0, &Pubkey::default()); @@ -669,7 +684,7 @@ mod tests { fn test_create_sysvar_invalid_id() { // Attempt to create system account in account already owned by another program let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); let to = Pubkey::new_rand(); let mut to_account = Account::default(); @@ -696,7 +711,7 @@ mod tests { // Attempt to create system account in account with populated data let new_program_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); let populated_key = Pubkey::new_rand(); let mut populated_account = Account { @@ -725,7 +740,7 @@ mod tests { #[test] fn test_create_from_account_is_nonce_fail() { let nonce = Pubkey::new_rand(); - let mut nonce_account = Account::new_data( + let mut nonce_account = Account::new_ref_data( 42, &nonce_state::NonceState::Initialized( nonce_state::Meta::new(&Pubkey::default()), @@ -783,6 +798,7 @@ mod tests { Ok(()) ); + let mut account = RefCell::new(account); assert_eq!( process_instruction( &Pubkey::default(), @@ -825,7 +841,7 @@ mod tests { assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &system_program::id()); + let mut from_account = Account::new_ref(100, 0, &system_program::id()); // Attempt to transfer with no destination let instruction = SystemInstruction::Transfer { lamports: 0 }; let data = serialize(&instruction).unwrap(); @@ -840,51 +856,40 @@ mod tests { #[test] fn test_transfer_lamports() { let from = Pubkey::new_rand(); - let mut from_account = Account::new(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter + let from_account = Account::new_ref(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter - transfer( - &mut KeyedAccount::new(&from, true, &mut from_account), - &mut to_account, - 50, - ) - .unwrap(); - let from_lamports = from_account.lamports; + let mut from_keyed_account = KeyedAccount::new(&from, true, &from_account); + transfer(&mut from_keyed_account, &mut to_account, 50).unwrap(); + let from_lamports = from_keyed_account.account.borrow().lamports; let to_lamports = to_account.lamports; assert_eq!(from_lamports, 50); assert_eq!(to_lamports, 51); // Attempt to move more lamports than remaining in from_account - let result = transfer( - &mut KeyedAccount::new(&from, true, &mut from_account), - &mut to_account, - 100, - ); + let mut from_keyed_account = KeyedAccount::new(&from, true, &from_account); + let result = transfer(&mut from_keyed_account, &mut to_account, 100); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); - assert_eq!(from_account.lamports, 50); + assert_eq!(from_keyed_account.account.borrow().lamports, 50); assert_eq!(to_account.lamports, 51); // test unsigned transfer of zero - assert!(transfer( - &mut KeyedAccount::new(&from, false, &mut from_account), - &mut to_account, - 0, - ) - .is_ok(),); - assert_eq!(from_account.lamports, 50); + let mut from_keyed_account = KeyedAccount::new(&from, false, &from_account); + assert!(transfer(&mut from_keyed_account, &mut to_account, 0,).is_ok(),); + assert_eq!(from_keyed_account.account.borrow().lamports, 50); assert_eq!(to_account.lamports, 51); } #[test] fn test_transfer_lamports_from_nonce_account_fail() { let from = Pubkey::new_rand(); - let mut from_account = Account::new_data( + let mut from_account = Account::new_ref_data( 100, &nonce_state::NonceState::Initialized(nonce_state::Meta::new(&from), Hash::default()), &system_program::id(), ) .unwrap(); assert_eq!( - get_system_account_kind(&from_account), + get_system_account_kind(&from_account.borrow()), Some(SystemAccountKind::Nonce) ); @@ -1009,7 +1014,7 @@ mod tests { .accounts .iter() .map(|meta| { - if sysvar::recent_blockhashes::check_id(&meta.pubkey) { + RefCell::new(if sysvar::recent_blockhashes::check_id(&meta.pubkey) { sysvar::recent_blockhashes::create_account_with_data( 1, vec![(0u64, &Hash::default()); 32].into_iter(), @@ -1018,7 +1023,7 @@ mod tests { sysvar::rent::create_account(1, &Rent::free()) } else { Account::default() - } + }) }) .collect(); @@ -1068,7 +1073,7 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), true, - &mut Account::default(), + &create_default_account(), ),], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), ), @@ -1082,11 +1087,11 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), true, &mut Account::default(),), + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut Account::default(), + &create_default_account(), ), ], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), @@ -1105,20 +1110,18 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), - ), - KeyedAccount::new( - &sysvar::rent::id(), - false, - &mut sysvar::rent::create_account(1, &Rent::free()), + &create_default_recent_blockhashes_account(), ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), ], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ) .unwrap(); + let new_recent_blockhashes_account = + RefCell::new(sysvar::recent_blockhashes::create_account_with_data( + 1, + vec![(0u64, &hash(&serialize(&0).unwrap())); 32].into_iter(), + )); assert_eq!( super::process_instruction( &Pubkey::default(), @@ -1127,10 +1130,7 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &hash(&serialize(&0).unwrap())); 32].into_iter(), - ), + &new_recent_blockhashes_account, ), ], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), @@ -1172,7 +1172,7 @@ mod tests { &mut [KeyedAccount::new( &Pubkey::default(), true, - &mut Account::default(), + &create_default_account() ),], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), @@ -1186,12 +1186,12 @@ mod tests { super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), true, &mut Account::default(),), - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default(),), + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &create_default_account()), KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut Account::default(), + &create_default_account() ), ], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), @@ -1211,16 +1211,13 @@ mod tests { true, &mut nonce_state::create_account(1_000_000), ), - KeyedAccount::new(&Pubkey::default(), true, &mut Account::default(),), + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), + &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &mut Account::default(),), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), ], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), @@ -1239,20 +1236,13 @@ mod tests { true, &mut nonce_state::create_account(1_000_000), ), - KeyedAccount::new(&Pubkey::default(), true, &mut Account::default(),), + KeyedAccount::new(&Pubkey::default(), true, &mut create_default_account()), KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), - ), - KeyedAccount::new( - &sysvar::rent::id(), - false, - &mut sysvar::rent::create_account(1, &Rent::free()) + &create_default_recent_blockhashes_account(), ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), ], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), @@ -1302,7 +1292,7 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut Account::default(), + &create_default_account() ), ], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), @@ -1325,12 +1315,9 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), + &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &mut Account::default(),), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), ], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), @@ -1352,16 +1339,9 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), - ), - KeyedAccount::new( - &sysvar::rent::id(), - false, - &mut sysvar::rent::create_account(1, &Rent::free()) + &create_default_recent_blockhashes_account(), ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), ], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), @@ -1379,16 +1359,9 @@ mod tests { KeyedAccount::new( &sysvar::recent_blockhashes::id(), false, - &mut sysvar::recent_blockhashes::create_account_with_data( - 1, - vec![(0u64, &Hash::default()); 32].into_iter(), - ), - ), - KeyedAccount::new( - &sysvar::rent::id(), - false, - &mut sysvar::rent::create_account(1, &Rent::free()), + &create_default_recent_blockhashes_account(), ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), ], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ) @@ -1444,7 +1417,7 @@ mod tests { #[test] fn test_get_system_account_kind_uninitialized_nonce_account_fail() { assert_eq!( - get_system_account_kind(&nonce_state::create_account(42)), + get_system_account_kind(&nonce_state::create_account(42).borrow()), None ); } diff --git a/sdk/src/account.rs b/sdk/src/account.rs index f03550132d..cbcce9672c 100644 --- a/sdk/src/account.rs +++ b/sdk/src/account.rs @@ -1,6 +1,12 @@ use crate::hash::Hash; +use crate::instruction::InstructionError; use crate::{clock::Epoch, pubkey::Pubkey}; -use std::{cmp, fmt, iter::FromIterator}; +use std::{ + cell::{Ref, RefCell, RefMut}, + cmp, fmt, + iter::FromIterator, + rc::Rc, +}; /// An Account with data that is stored on chain #[repr(C)] @@ -69,6 +75,9 @@ impl Account { ..Self::default() } } + pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc> { + Rc::new(RefCell::new(Self::new(lamports, space, owner))) + } pub fn new_data( lamports: u64, @@ -83,6 +92,13 @@ impl Account { ..Self::default() }) } + pub fn new_ref_data( + lamports: u64, + state: &T, + owner: &Pubkey, + ) -> Result, bincode::Error> { + Ok(RefCell::new(Self::new_data(lamports, state, owner)?)) + } pub fn new_data_with_space( lamports: u64, @@ -96,6 +112,16 @@ impl Account { Ok(account) } + pub fn new_ref_data_with_space( + lamports: u64, + state: &T, + space: usize, + owner: &Pubkey, + ) -> Result, bincode::Error> { + Ok(RefCell::new(Self::new_data_with_space( + lamports, state, space, owner, + )?)) + } pub fn deserialize_data(&self) -> Result { bincode::deserialize(&self.data) @@ -115,7 +141,7 @@ pub struct KeyedAccount<'a> { is_signer: bool, // Transaction was signed by this account's key is_writable: bool, key: &'a Pubkey, - pub account: &'a mut Account, + pub account: &'a RefCell, } impl<'a> KeyedAccount<'a> { @@ -135,7 +161,46 @@ impl<'a> KeyedAccount<'a> { self.is_writable } - pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a mut Account) -> Self { + pub fn lamports(&self) -> Result { + Ok(self.try_borrow()?.lamports) + } + + pub fn data_len(&self) -> Result { + Ok(self.try_borrow()?.data.len()) + } + + pub fn data_is_empty(&self) -> Result { + Ok(self.try_borrow()?.data.is_empty()) + } + + pub fn owner(&self) -> Result { + Ok(self.try_borrow()?.owner) + } + + pub fn executable(&self) -> Result { + Ok(self.try_borrow()?.executable) + } + + pub fn try_account_ref(&'a self) -> Result, InstructionError> { + self.try_borrow() + } + + pub fn try_account_ref_mut(&'a self) -> Result, InstructionError> { + self.try_borrow_mut() + } + + fn try_borrow(&self) -> Result, InstructionError> { + self.account + .try_borrow() + .map_err(|_| InstructionError::AccountBorrowFailed) + } + fn try_borrow_mut(&self) -> Result, InstructionError> { + self.account + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowFailed) + } + + pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a RefCell) -> Self { Self { is_signer, is_writable: true, @@ -144,7 +209,7 @@ impl<'a> KeyedAccount<'a> { } } - pub fn new_readonly(key: &'a Pubkey, is_signer: bool, account: &'a mut Account) -> Self { + pub fn new_readonly(key: &'a Pubkey, is_signer: bool, account: &'a RefCell) -> Self { Self { is_signer, is_writable: false, @@ -154,8 +219,14 @@ impl<'a> KeyedAccount<'a> { } } -impl<'a> From<(&'a Pubkey, &'a mut Account)> for KeyedAccount<'a> { - fn from((key, account): (&'a Pubkey, &'a mut Account)) -> Self { +impl<'a> PartialEq for KeyedAccount<'a> { + fn eq(&self, other: &Self) -> bool { + self.key == other.key + } +} + +impl<'a> From<(&'a Pubkey, &'a RefCell)> for KeyedAccount<'a> { + fn from((key, account): (&'a Pubkey, &'a RefCell)) -> Self { Self { is_signer: false, is_writable: true, @@ -165,8 +236,8 @@ impl<'a> From<(&'a Pubkey, &'a mut Account)> for KeyedAccount<'a> { } } -impl<'a> From<(&'a Pubkey, bool, &'a mut Account)> for KeyedAccount<'a> { - fn from((key, is_signer, account): (&'a Pubkey, bool, &'a mut Account)) -> Self { +impl<'a> From<(&'a Pubkey, bool, &'a RefCell)> for KeyedAccount<'a> { + fn from((key, is_signer, account): (&'a Pubkey, bool, &'a RefCell)) -> Self { Self { is_signer, is_writable: true, @@ -176,8 +247,8 @@ impl<'a> From<(&'a Pubkey, bool, &'a mut Account)> for KeyedAccount<'a> { } } -impl<'a> From<&'a mut (Pubkey, Account)> for KeyedAccount<'a> { - fn from((key, account): &'a mut (Pubkey, Account)) -> Self { +impl<'a> From<&'a mut (&'a Pubkey, &'a RefCell)> for KeyedAccount<'a> { + fn from((key, account): &'a mut (&'a Pubkey, &'a RefCell)) -> Self { Self { is_signer: false, is_writable: true, @@ -187,12 +258,14 @@ impl<'a> From<&'a mut (Pubkey, Account)> for KeyedAccount<'a> { } } -pub fn create_keyed_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec { +pub fn create_keyed_accounts<'a>( + accounts: &'a mut [(&'a Pubkey, &'a RefCell)], +) -> Vec> { accounts.iter_mut().map(Into::into).collect() } pub fn create_keyed_is_signer_accounts<'a>( - accounts: &'a mut [(&'a Pubkey, bool, &'a mut Account)], + accounts: &'a mut [(&'a Pubkey, bool, &'a mut RefCell)], ) -> Vec> { accounts .iter_mut() @@ -205,7 +278,9 @@ pub fn create_keyed_is_signer_accounts<'a>( .collect() } -pub fn create_keyed_readonly_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec { +pub fn create_keyed_readonly_accounts( + accounts: &mut [(Pubkey, RefCell)], +) -> Vec { accounts .iter_mut() .map(|(key, account)| KeyedAccount { diff --git a/sdk/src/account_utils.rs b/sdk/src/account_utils.rs index 28dcf46b63..5a47ac6c49 100644 --- a/sdk/src/account_utils.rs +++ b/sdk/src/account_utils.rs @@ -30,10 +30,10 @@ where T: serde::Serialize + serde::de::DeserializeOwned, { fn state(&self) -> Result { - self.account.state() + self.try_account_ref()?.state() } fn set_state(&mut self, state: &T) -> Result<(), InstructionError> { - self.account.set_state(state) + self.try_account_ref_mut()?.set_state(state) } } diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index ecb597fe60..e67505a9c7 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -59,6 +59,7 @@ pub enum InstructionError { ReadonlyDataModified, /// An account was referenced more than once in a single instruction + // Deprecated, instructions can now contain duplicate accounts DuplicateAccountIndex, /// Executable bit on account changed, but shouldn't have @@ -76,6 +77,17 @@ pub enum InstructionError { /// The instruction expected an executable account AccountNotExecutable, + /// Failed to borrow a reference to an account, already borrowed + AccountBorrowFailed, + + /// Account has an outstanding reference after a program's execution + AccountBorrowOutstanding, + + /// The same account was multiply passed to an on-chain program's entrypoint, but the program + /// modified them differently. A program can only modify one instance of the account because + /// the runtime cannot determine which changes to pick or how to merge them if both are modified + DuplicateAccountOutOfSync, + /// CustomError allows on-chain programs to implement program-specific error types and see /// them returned by the Solana runtime. A CustomError may be any type that is represented /// as or serialized to a u32 integer. diff --git a/sdk/src/instruction_processor_utils.rs b/sdk/src/instruction_processor_utils.rs index b8bbbae59d..0917e9721d 100644 --- a/sdk/src/instruction_processor_utils.rs +++ b/sdk/src/instruction_processor_utils.rs @@ -115,8 +115,8 @@ pub fn next_keyed_account(iter: &mut I) -> Result bool { - !keyed_accounts.is_empty() && keyed_accounts[0].account.executable +pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> Result { + Ok(!keyed_accounts.is_empty() && keyed_accounts[0].executable()?) } pub fn limited_deserialize(instruction_data: &[u8]) -> Result diff --git a/sdk/src/nonce_state.rs b/sdk/src/nonce_state.rs index a56b632a6e..dc76fccc65 100644 --- a/sdk/src/nonce_state.rs +++ b/sdk/src/nonce_state.rs @@ -10,7 +10,7 @@ use crate::{ sysvar::rent::Rent, }; use serde_derive::{Deserialize, Serialize}; -use std::collections::HashSet; +use std::{cell::RefCell, collections::HashSet}; #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy)] pub struct Meta { @@ -107,19 +107,19 @@ impl<'a> NonceAccount for KeyedAccount<'a> { ) -> Result<(), InstructionError> { let signer = match self.state()? { NonceState::Uninitialized => { - if lamports > self.account.lamports { + if lamports > self.lamports()? { return Err(InstructionError::InsufficientFunds); } *self.unsigned_key() } NonceState::Initialized(meta, ref hash) => { - if lamports == self.account.lamports { + if lamports == self.lamports()? { if *hash == recent_blockhashes[0] { return Err(NonceError::NotExpired.into()); } } else { - let min_balance = rent.minimum_balance(self.account.data.len()); - if lamports + min_balance > self.account.lamports { + let min_balance = rent.minimum_balance(self.data_len()?); + if lamports + min_balance > self.lamports()? { return Err(InstructionError::InsufficientFunds); } } @@ -131,8 +131,8 @@ impl<'a> NonceAccount for KeyedAccount<'a> { return Err(InstructionError::MissingRequiredSignature); } - self.account.lamports -= lamports; - to.account.lamports += lamports; + self.try_account_ref_mut()?.lamports -= lamports; + to.try_account_ref_mut()?.lamports += lamports; Ok(()) } @@ -149,8 +149,8 @@ impl<'a> NonceAccount for KeyedAccount<'a> { let meta = match self.state()? { NonceState::Uninitialized => { - let min_balance = rent.minimum_balance(self.account.data.len()); - if self.account.lamports < min_balance { + let min_balance = rent.minimum_balance(self.data_len()?); + if self.lamports()? < min_balance { return Err(InstructionError::InsufficientFunds); } Meta::new(nonce_authority) @@ -178,14 +178,16 @@ impl<'a> NonceAccount for KeyedAccount<'a> { } } -pub fn create_account(lamports: u64) -> Account { - Account::new_data_with_space( - lamports, - &NonceState::Uninitialized, - NonceState::size(), - &system_program::id(), +pub fn create_account(lamports: u64) -> RefCell { + RefCell::new( + Account::new_data_with_space( + lamports, + &NonceState::Uninitialized, + NonceState::size(), + &system_program::id(), + ) + .expect("nonce_account"), ) - .expect("nonce_account") } /// Convenience function for working with keyed accounts in tests @@ -195,8 +197,8 @@ where F: FnMut(&mut KeyedAccount), { let pubkey = Pubkey::new_rand(); - let mut account = create_account(lamports); - let mut keyed_account = KeyedAccount::new(&pubkey, signer, &mut account); + let account = create_account(lamports); + let mut keyed_account = KeyedAccount::new(&pubkey, signer, &account); f(&mut keyed_account) } @@ -262,9 +264,10 @@ mod test { assert_eq!(state, NonceState::Initialized(meta, stored)); with_test_keyed_account(42, false, |mut to_keyed| { let recent_blockhashes = create_test_recent_blockhashes(0); - let withdraw_lamports = keyed_account.account.lamports; - let expect_nonce_lamports = keyed_account.account.lamports - withdraw_lamports; - let expect_to_lamports = to_keyed.account.lamports + withdraw_lamports; + let withdraw_lamports = keyed_account.account.borrow().lamports; + let expect_nonce_lamports = + keyed_account.account.borrow().lamports - withdraw_lamports; + let expect_to_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; keyed_account .nonce_withdraw( withdraw_lamports, @@ -275,9 +278,12 @@ mod test { ) .unwrap(); // Empties NonceAccount balance - assert_eq!(keyed_account.account.lamports, expect_nonce_lamports); + assert_eq!( + keyed_account.account.borrow().lamports, + expect_nonce_lamports + ); // NonceAccount balance goes to `to` - assert_eq!(to_keyed.account.lamports, expect_to_lamports); + assert_eq!(to_keyed.account.borrow().lamports, expect_to_lamports); }) }) } @@ -297,7 +303,7 @@ mod test { nonce_account .nonce_initialize(&authorized, &recent_blockhashes, &rent) .unwrap(); - let pubkey = nonce_account.account.owner.clone(); + let pubkey = nonce_account.account.borrow().owner.clone(); let mut nonce_account = KeyedAccount::new(&pubkey, false, nonce_account.account); let state: NonceState = nonce_account.state().unwrap(); assert_eq!(state, NonceState::Initialized(meta, stored)); @@ -426,9 +432,10 @@ mod test { let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); let recent_blockhashes = create_test_recent_blockhashes(0); - let withdraw_lamports = nonce_keyed.account.lamports; - let expect_nonce_lamports = nonce_keyed.account.lamports - withdraw_lamports; - let expect_to_lamports = to_keyed.account.lamports + withdraw_lamports; + let withdraw_lamports = nonce_keyed.account.borrow().lamports; + let expect_nonce_lamports = + nonce_keyed.account.borrow().lamports - withdraw_lamports; + let expect_to_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; nonce_keyed .nonce_withdraw( withdraw_lamports, @@ -443,9 +450,9 @@ mod test { // Deinitializes NonceAccount state assert_eq!(state, NonceState::Uninitialized); // Empties NonceAccount balance - assert_eq!(nonce_keyed.account.lamports, expect_nonce_lamports); + assert_eq!(nonce_keyed.account.borrow().lamports, expect_nonce_lamports); // NonceAccount balance goes to `to` - assert_eq!(to_keyed.account.lamports, expect_to_lamports); + assert_eq!(to_keyed.account.borrow().lamports, expect_to_lamports); }) }) } @@ -463,8 +470,9 @@ mod test { with_test_keyed_account(42, false, |mut to_keyed| { let signers = HashSet::new(); let recent_blockhashes = create_test_recent_blockhashes(0); + let lamports = nonce_keyed.account.borrow().lamports; let result = nonce_keyed.nonce_withdraw( - nonce_keyed.account.lamports, + lamports, &mut to_keyed, &recent_blockhashes, &rent, @@ -489,8 +497,9 @@ mod test { let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); let recent_blockhashes = create_test_recent_blockhashes(0); + let lamports = nonce_keyed.account.borrow().lamports + 1; let result = nonce_keyed.nonce_withdraw( - nonce_keyed.account.lamports + 1, + lamports, &mut to_keyed, &recent_blockhashes, &rent, @@ -513,9 +522,10 @@ mod test { let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); let recent_blockhashes = create_test_recent_blockhashes(0); - let withdraw_lamports = nonce_keyed.account.lamports / 2; - let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports; - let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports; + let withdraw_lamports = nonce_keyed.account.borrow().lamports / 2; + let nonce_expect_lamports = + nonce_keyed.account.borrow().lamports - withdraw_lamports; + let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; nonce_keyed .nonce_withdraw( withdraw_lamports, @@ -527,11 +537,12 @@ mod test { .unwrap(); let state: NonceState = nonce_keyed.state().unwrap(); assert_eq!(state, NonceState::Uninitialized); - assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports); - assert_eq!(to_keyed.account.lamports, to_expect_lamports); - let withdraw_lamports = nonce_keyed.account.lamports; - let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports; - let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports; + assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports); + assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports); + let withdraw_lamports = nonce_keyed.account.borrow().lamports; + let nonce_expect_lamports = + nonce_keyed.account.borrow().lamports - withdraw_lamports; + let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; nonce_keyed .nonce_withdraw( withdraw_lamports, @@ -543,8 +554,8 @@ mod test { .unwrap(); let state: NonceState = nonce_keyed.state().unwrap(); assert_eq!(state, NonceState::Uninitialized); - assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports); - assert_eq!(to_keyed.account.lamports, to_expect_lamports); + assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports); + assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports); }) }) } @@ -569,9 +580,10 @@ mod test { let stored = recent_blockhashes[0]; assert_eq!(state, NonceState::Initialized(meta, stored)); with_test_keyed_account(42, false, |mut to_keyed| { - let withdraw_lamports = nonce_keyed.account.lamports - min_lamports; - let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports; - let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports; + let withdraw_lamports = nonce_keyed.account.borrow().lamports - min_lamports; + let nonce_expect_lamports = + nonce_keyed.account.borrow().lamports - withdraw_lamports; + let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; nonce_keyed .nonce_withdraw( withdraw_lamports, @@ -584,12 +596,13 @@ mod test { let state: NonceState = nonce_keyed.state().unwrap(); let stored = recent_blockhashes[0]; assert_eq!(state, NonceState::Initialized(meta, stored)); - assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports); - assert_eq!(to_keyed.account.lamports, to_expect_lamports); + assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports); + assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports); let recent_blockhashes = create_test_recent_blockhashes(0); - let withdraw_lamports = nonce_keyed.account.lamports; - let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports; - let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports; + let withdraw_lamports = nonce_keyed.account.borrow().lamports; + let nonce_expect_lamports = + nonce_keyed.account.borrow().lamports - withdraw_lamports; + let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports; nonce_keyed .nonce_withdraw( withdraw_lamports, @@ -599,8 +612,8 @@ mod test { &signers, ) .unwrap(); - assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports); - assert_eq!(to_keyed.account.lamports, to_expect_lamports); + assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports); + assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports); }) }) } @@ -621,7 +634,7 @@ mod test { with_test_keyed_account(42, false, |mut to_keyed| { let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); - let withdraw_lamports = nonce_keyed.account.lamports; + let withdraw_lamports = nonce_keyed.account.borrow().lamports; let result = nonce_keyed.nonce_withdraw( withdraw_lamports, &mut to_keyed, @@ -651,7 +664,7 @@ mod test { let recent_blockhashes = create_test_recent_blockhashes(63); let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); - let withdraw_lamports = nonce_keyed.account.lamports + 1; + let withdraw_lamports = nonce_keyed.account.borrow().lamports + 1; let result = nonce_keyed.nonce_withdraw( withdraw_lamports, &mut to_keyed, @@ -681,7 +694,7 @@ mod test { let recent_blockhashes = create_test_recent_blockhashes(63); let mut signers = HashSet::new(); signers.insert(nonce_keyed.signer_key().unwrap().clone()); - let withdraw_lamports = nonce_keyed.account.lamports - min_lamports + 1; + let withdraw_lamports = nonce_keyed.account.borrow().lamports - min_lamports + 1; let result = nonce_keyed.nonce_withdraw( withdraw_lamports, &mut to_keyed, diff --git a/sdk/src/sysvar/mod.rs b/sdk/src/sysvar/mod.rs index a5bdb7edf6..b1b58ac001 100644 --- a/sdk/src/sysvar/mod.rs +++ b/sdk/src/sysvar/mod.rs @@ -80,7 +80,8 @@ pub trait Sysvar: if !Self::check_id(keyed_account.unsigned_key()) { return Err(InstructionError::InvalidArgument); } - Self::from_account(keyed_account.account).ok_or(InstructionError::InvalidArgument) + Self::from_account(&*keyed_account.try_account_ref()?) + .ok_or(InstructionError::InvalidArgument) } fn create_account(&self, lamports: u64) -> Account { let data_len = Self::size_of().max(bincode::serialized_size(self).unwrap() as usize); diff --git a/sdk/src/sysvar/rent.rs b/sdk/src/sysvar/rent.rs index 5a5069cbe7..ff005762d9 100644 --- a/sdk/src/sysvar/rent.rs +++ b/sdk/src/sysvar/rent.rs @@ -21,10 +21,7 @@ pub fn verify_rent_exemption( rent_sysvar_account: &KeyedAccount, ) -> Result<(), InstructionError> { let rent = Rent::from_keyed_account(rent_sysvar_account)?; - if !rent.is_exempt( - keyed_account.account.lamports, - keyed_account.account.data.len(), - ) { + if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) { Err(InstructionError::InsufficientFunds) } else { Ok(()) diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index 6da812d73f..eb6cea1e9d 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -601,4 +601,29 @@ mod tests { ); assert!(tx.is_signed()); } + + #[test] + fn test_transaction_instruction_with_duplicate_keys() { + let program_id = Pubkey::default(); + let keypair0 = Keypair::new(); + let id0 = keypair0.pubkey(); + let id1 = Pubkey::new_rand(); + let ix = Instruction::new( + program_id, + &0, + vec![ + AccountMeta::new(id0, true), + AccountMeta::new(id1, false), + AccountMeta::new(id0, false), + AccountMeta::new(id1, false), + ], + ); + let mut tx = Transaction::new_unsigned_instructions(vec![ix]); + tx.sign(&[&keypair0], Hash::default()); + assert_eq!( + tx.message.instructions[0], + CompiledInstruction::new(2, &0, vec![0, 1, 0, 1]) + ); + assert!(tx.is_signed()); + } }