From fbcbd376509b6f4f2780c61f153f3455720e4325 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 3 Jun 2020 16:34:59 -0700 Subject: [PATCH] v1.1: Enable rolling update of "Permit paying oneself" / "No longer allow create-account to add funds to an existing account" (#10394) automerge --- runtime/src/bank.rs | 106 +- runtime/src/builtin_programs.rs | 78 + .../legacy_system_instruction_processor0.rs | 1498 +++++++++++++++++ runtime/src/lib.rs | 2 + 4 files changed, 1663 insertions(+), 21 deletions(-) create mode 100644 runtime/src/builtin_programs.rs create mode 100644 runtime/src/legacy_system_instruction_processor0.rs diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 32aaccb6cb0e20..9a49ad77fe4995 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -10,6 +10,7 @@ use crate::{ accounts_db::{AccountsDBSerialize, ErrorCounters, SnapshotStorage, SnapshotStorages}, accounts_index::Ancestors, blockhash_queue::BlockhashQueue, + builtin_programs::{get_builtin_programs, get_epoch_activated_builtin_programs}, epoch_stakes::{EpochStakes, NodeVoteAccounts}, message_processor::{MessageProcessor, ProcessInstruction}, nonce_utils, @@ -21,7 +22,7 @@ use crate::{ status_cache::{SlotDelta, StatusCache}, storage_utils, storage_utils::StorageAccounts, - system_instruction_processor::{self, get_system_account_kind, SystemAccountKind}, + system_instruction_processor::{get_system_account_kind, SystemAccountKind}, transaction_batch::TransactionBatch, transaction_utils::OrderedIterator, }; @@ -513,6 +514,13 @@ impl Bank { } } + if let Some(builtin_programs) = + get_epoch_activated_builtin_programs(new.operating_mode(), new.epoch) + { + for program in builtin_programs.iter() { + new.add_static_program(&program.name, program.id, program.process_instruction); + } + } new.update_epoch_stakes(leader_schedule_epoch); new.ancestors.insert(new.slot(), 0); new.parents().iter().enumerate().for_each(|(i, p)| { @@ -2123,26 +2131,10 @@ impl Bank { } pub fn finish_init(&mut self) { - self.add_static_program( - "system_program", - solana_sdk::system_program::id(), - system_instruction_processor::process_instruction, - ); - self.add_static_program( - "config_program", - solana_config_program::id(), - solana_config_program::config_processor::process_instruction, - ); - self.add_static_program( - "stake_program", - solana_stake_program::id(), - solana_stake_program::stake_instruction::process_instruction, - ); - self.add_static_program( - "vote_program", - solana_vote_program::id(), - solana_vote_program::vote_instruction::process_instruction, - ); + let builtin_programs = get_builtin_programs(self.operating_mode(), self.epoch); + for program in builtin_programs.iter() { + self.add_static_program(&program.name, program.id, program.process_instruction); + } } pub fn set_parent(&mut self, parent: &Arc) { @@ -2659,6 +2651,7 @@ mod tests { use crate::{ accounts_db::{get_temp_accounts_paths, tests::copy_append_vecs}, accounts_index::Ancestors, + builtin_programs::new_system_program_activation_epoch, genesis_utils::{ create_genesis_config_with_leader, GenesisConfigInfo, BOOTSTRAP_VALIDATOR_LAMPORTS, }, @@ -6823,6 +6816,77 @@ mod tests { assert_eq!(bank.capitalization(), pre_capitalization - burn_amount); } + #[test] + fn test_legacy_system_instruction_processor0_stable() { + let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000); + genesis_config.operating_mode = OperatingMode::Stable; + let bank0 = Arc::new(Bank::new(&genesis_config)); + + let activation_epoch = new_system_program_activation_epoch(bank0.operating_mode()); + assert!(activation_epoch > bank0.epoch()); + + // Transfer to self is not supported by legacy_system_instruction_processor0 + bank0 + .transfer(1, &mint_keypair, &mint_keypair.pubkey()) + .unwrap_err(); + + // Activate system_instruction_processor + let bank = Bank::new_from_parent( + &bank0, + &Pubkey::default(), + genesis_config + .epoch_schedule + .get_first_slot_in_epoch(activation_epoch), + ); + + // Transfer to self is supported by system_instruction_processor + bank.transfer(2, &mint_keypair, &mint_keypair.pubkey()) + .unwrap(); + } + + #[test] + fn test_legacy_system_instruction_processor0_preview() { + let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000); + genesis_config.operating_mode = OperatingMode::Preview; + let bank0 = Arc::new(Bank::new(&genesis_config)); + + let activation_epoch = new_system_program_activation_epoch(bank0.operating_mode()); + assert!(activation_epoch > bank0.epoch()); + + // Transfer to self is not supported by legacy_system_instruction_processor0 + bank0 + .transfer(1, &mint_keypair, &mint_keypair.pubkey()) + .unwrap_err(); + + // Activate system_instruction_processor + let bank = Bank::new_from_parent( + &bank0, + &Pubkey::default(), + genesis_config + .epoch_schedule + .get_first_slot_in_epoch(activation_epoch), + ); + + // Transfer to self is supported by system_instruction_processor + bank.transfer(2, &mint_keypair, &mint_keypair.pubkey()) + .unwrap(); + } + + #[test] + fn test_legacy_system_instruction_processor0_development() { + let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000); + genesis_config.operating_mode = OperatingMode::Development; + let bank0 = Arc::new(Bank::new(&genesis_config)); + + let activation_epoch = new_system_program_activation_epoch(bank0.operating_mode()); + assert!(activation_epoch == bank0.epoch()); + + // Transfer to self is supported by system_instruction_processor + bank0 + .transfer(2, &mint_keypair, &mint_keypair.pubkey()) + .unwrap(); + } + #[test] fn test_duplicate_account_key() { solana_logger::setup(); diff --git a/runtime/src/builtin_programs.rs b/runtime/src/builtin_programs.rs new file mode 100644 index 00000000000000..1dfafee0db3bc2 --- /dev/null +++ b/runtime/src/builtin_programs.rs @@ -0,0 +1,78 @@ +use crate::{ + legacy_system_instruction_processor0, message_processor::ProcessInstruction, + system_instruction_processor, +}; +use solana_sdk::{clock::Epoch, genesis_config::OperatingMode, pubkey::Pubkey, system_program}; + +pub struct BuiltinProgram { + pub name: String, + pub id: Pubkey, + pub process_instruction: ProcessInstruction, +} +impl BuiltinProgram { + pub fn new(name: &str, id: Pubkey, process_instruction: ProcessInstruction) -> Self { + Self { + name: name.to_string(), + id, + process_instruction, + } + } +} + +pub(crate) fn new_system_program_activation_epoch(operating_mode: OperatingMode) -> Epoch { + match operating_mode { + OperatingMode::Development => 0, + OperatingMode::Preview => 1_000_000, + OperatingMode::Stable => 1_000_000, + } +} + +/// All builtin programs that should be active at the given (operating_mode, epoch) +pub fn get_builtin_programs(operating_mode: OperatingMode, epoch: Epoch) -> Vec { + vec![ + if epoch < new_system_program_activation_epoch(operating_mode) { + BuiltinProgram::new( + "system_program", + system_program::id(), + legacy_system_instruction_processor0::process_instruction, + ) + } else { + BuiltinProgram::new( + "system_program", + system_program::id(), + system_instruction_processor::process_instruction, + ) + }, + BuiltinProgram::new( + "config_program", + solana_config_program::id(), + solana_config_program::config_processor::process_instruction, + ), + BuiltinProgram::new( + "stake_program", + solana_stake_program::id(), + solana_stake_program::stake_instruction::process_instruction, + ), + BuiltinProgram::new( + "vote_program", + solana_vote_program::id(), + solana_vote_program::vote_instruction::process_instruction, + ), + ] +} + +/// Builtin programs that activate at the given (operating_mode, epoch) +pub fn get_epoch_activated_builtin_programs( + operating_mode: OperatingMode, + epoch: Epoch, +) -> Option> { + if epoch == new_system_program_activation_epoch(operating_mode) { + Some(vec![BuiltinProgram::new( + "system_program", + system_program::id(), + system_instruction_processor::process_instruction, + )]) + } else { + None + } +} diff --git a/runtime/src/legacy_system_instruction_processor0.rs b/runtime/src/legacy_system_instruction_processor0.rs new file mode 100644 index 00000000000000..8919747183d7f9 --- /dev/null +++ b/runtime/src/legacy_system_instruction_processor0.rs @@ -0,0 +1,1498 @@ +use log::*; +use solana_sdk::{ + account::{get_signers, Account, KeyedAccount}, + instruction::InstructionError, + nonce::Account as NonceAccount, + program_utils::{limited_deserialize, next_keyed_account}, + pubkey::Pubkey, + system_instruction::{SystemError, SystemInstruction, MAX_PERMITTED_DATA_LENGTH}, + system_program, + sysvar::{self, recent_blockhashes::RecentBlockhashes, rent::Rent, Sysvar}, +}; +use std::collections::HashSet; + +// represents an address that may or may not have been generated +// from a seed +#[derive(PartialEq, Default, Debug)] +struct Address { + address: Pubkey, + base: Option, +} + +impl Address { + fn is_signer(&self, signers: &HashSet) -> bool { + if let Some(base) = self.base { + signers.contains(&base) + } else { + signers.contains(&self.address) + } + } + fn create( + address: &Pubkey, + with_seed: Option<(&Pubkey, &str, &Pubkey)>, + ) -> Result { + let base = if let Some((base, seed, program_id)) = with_seed { + // re-derive the address, must match the supplied address + if *address != Pubkey::create_with_seed(base, seed, program_id)? { + return Err(SystemError::AddressWithSeedMismatch.into()); + } + Some(*base) + } else { + None + }; + + Ok(Self { + address: *address, + base, + }) + } +} + +fn allocate( + account: &mut Account, + address: &Address, + space: u64, + signers: &HashSet, +) -> Result<(), InstructionError> { + if !address.is_signer(signers) { + debug!("Allocate: must carry signature of `to`"); + return Err(InstructionError::MissingRequiredSignature); + } + + // if it looks like the `to` account is already in use, bail + // (note that the id check is also enforced by message_processor) + if !account.data.is_empty() || !system_program::check_id(&account.owner) { + debug!( + "Allocate: invalid argument; account {:?} already in use", + address + ); + return Err(SystemError::AccountAlreadyInUse.into()); + } + + if space > MAX_PERMITTED_DATA_LENGTH { + debug!( + "Allocate: requested space: {} is more than maximum allowed", + space + ); + return Err(SystemError::InvalidAccountDataLength.into()); + } + + account.data = vec![0; space as usize]; + + Ok(()) +} + +fn assign( + account: &mut Account, + address: &Address, + program_id: &Pubkey, + signers: &HashSet, +) -> Result<(), InstructionError> { + // no work to do, just return + if account.owner == *program_id { + return Ok(()); + } + + if !address.is_signer(&signers) { + debug!("Assign: account must sign"); + return Err(InstructionError::MissingRequiredSignature); + } + + // guard against sysvars being made + if sysvar::check_id(&program_id) { + debug!("Assign: program id {} invalid", program_id); + return Err(SystemError::InvalidProgramId.into()); + } + + account.owner = *program_id; + Ok(()) +} + +fn allocate_and_assign( + to: &mut Account, + to_address: &Address, + space: u64, + program_id: &Pubkey, + signers: &HashSet, +) -> Result<(), InstructionError> { + allocate(to, to_address, space, signers)?; + assign(to, to_address, program_id, signers) +} + +fn create_account( + from: &KeyedAccount, + to: &mut Account, + to_address: &Address, + lamports: u64, + space: u64, + program_id: &Pubkey, + signers: &HashSet, +) -> Result<(), InstructionError> { + allocate_and_assign(to, to_address, space, program_id, signers)?; + transfer(from, to, lamports) +} + +fn transfer(from: &KeyedAccount, to: &mut Account, lamports: u64) -> Result<(), InstructionError> { + if lamports == 0 { + return Ok(()); + } + + if from.signer_key().is_none() { + debug!("Transfer: from must sign"); + return Err(InstructionError::MissingRequiredSignature); + } + + if !from.data_is_empty()? { + debug!("Transfer: `from` must not carry data"); + return Err(InstructionError::InvalidArgument); + } + if lamports > from.lamports()? { + debug!( + "Transfer: insufficient lamports ({}, need {})", + from.lamports()?, + lamports + ); + return Err(SystemError::ResultWithNegativeLamports.into()); + } + + from.try_account_ref_mut()?.lamports -= lamports; + to.lamports += lamports; + Ok(()) +} + +pub fn process_instruction( + _program_id: &Pubkey, + keyed_accounts: &[KeyedAccount], + instruction_data: &[u8], +) -> Result<(), InstructionError> { + let instruction = limited_deserialize(instruction_data)?; + + trace!("process_instruction: {:?}", instruction); + trace!("keyed_accounts: {:?}", keyed_accounts); + + let signers = get_signers(keyed_accounts); + let keyed_accounts_iter = &mut keyed_accounts.iter(); + + match instruction { + SystemInstruction::CreateAccount { + lamports, + space, + program_id, + } => { + 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, + &mut to_account, + &to_address, + lamports, + space, + &program_id, + &signers, + ) + } + SystemInstruction::CreateAccountWithSeed { + base, + seed, + lamports, + space, + program_id, + } => { + 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, + &mut to_account, + &to_address, + lamports, + space, + &program_id, + &signers, + ) + } + 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(&mut account, &address, &program_id, &signers) + } + SystemInstruction::Transfer { lamports } => { + let from = next_keyed_account(keyed_accounts_iter)?; + 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)?; + me.advance_nonce_account( + &RecentBlockhashes::from_keyed_account(next_keyed_account(keyed_accounts_iter)?)?, + &signers, + ) + } + SystemInstruction::WithdrawNonceAccount(lamports) => { + let me = &mut next_keyed_account(keyed_accounts_iter)?; + let to = &mut next_keyed_account(keyed_accounts_iter)?; + me.withdraw_nonce_account( + lamports, + to, + &RecentBlockhashes::from_keyed_account(next_keyed_account(keyed_accounts_iter)?)?, + &Rent::from_keyed_account(next_keyed_account(keyed_accounts_iter)?)?, + &signers, + ) + } + SystemInstruction::InitializeNonceAccount(authorized) => { + let me = &mut next_keyed_account(keyed_accounts_iter)?; + me.initialize_nonce_account( + &authorized, + &RecentBlockhashes::from_keyed_account(next_keyed_account(keyed_accounts_iter)?)?, + &Rent::from_keyed_account(next_keyed_account(keyed_accounts_iter)?)?, + ) + } + SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { + let me = &mut next_keyed_account(keyed_accounts_iter)?; + me.authorize_nonce_account(&nonce_authority, &signers) + } + 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(&mut account, &address, space, &signers) + } + SystemInstruction::AllocateWithSeed { + base, + seed, + space, + 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(&mut account, &address, space, &program_id, &signers) + } + SystemInstruction::AssignWithSeed { + base, + seed, + 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(&mut account, &address, &program_id, &signers) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{bank::Bank, bank_client::BankClient}; + use bincode::serialize; + use solana_sdk::{ + account::Account, + account_utils::StateMut, + client::SyncClient, + fee_calculator::FeeCalculator, + genesis_config::create_genesis_config, + hash::{hash, Hash}, + instruction::{AccountMeta, Instruction, InstructionError}, + message::Message, + nonce, + signature::{Keypair, Signer}, + system_instruction, system_program, sysvar, + sysvar::recent_blockhashes::IterItem, + transaction::TransactionError, + }; + use std::cell::RefCell; + use std::sync::Arc; + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + enum SystemAccountKind { + System, + Nonce, + } + + fn get_system_account_kind(account: &Account) -> Option { + if system_program::check_id(&account.owner) { + if account.data.is_empty() { + Some(SystemAccountKind::System) + } else if account.data.len() == nonce::State::size() { + match account.state().ok()? { + nonce::state::Versions::Current(state) => match *state { + nonce::State::Initialized(_) => Some(SystemAccountKind::Nonce), + _ => None, + }, + } + } else { + None + } + } else { + None + } + } + + impl From for Address { + fn from(address: Pubkey) -> Self { + Self { + address, + base: None, + } + } + } + + 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![ + IterItem(0u64, &Hash::default(), &FeeCalculator::default()); + sysvar::recent_blockhashes::MAX_ENTRIES + ] + .into_iter(), + )) + } + fn create_default_rent_account() -> RefCell { + RefCell::new(sysvar::rent::create_account(1, &Rent::free())) + } + + #[test] + fn test_create_account() { + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + let to = Pubkey::new_rand(); + let from_account = Account::new_ref(100, 0, &system_program::id()); + let to_account = Account::new_ref(0, 0, &Pubkey::default()); + + assert_eq!( + process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&from, true, &from_account), + KeyedAccount::new(&to, true, &to_account) + ], + &bincode::serialize(&SystemInstruction::CreateAccount { + lamports: 50, + space: 2, + program_id: new_program_owner + }) + .unwrap() + ), + Ok(()) + ); + 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] + fn test_create_account_with_seed() { + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + let seed = "shiny pepper"; + let to = Pubkey::create_with_seed(&from, seed, &new_program_owner).unwrap(); + + let from_account = Account::new_ref(100, 0, &system_program::id()); + let to_account = Account::new_ref(0, 0, &Pubkey::default()); + + assert_eq!( + process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&from, true, &from_account), + KeyedAccount::new(&to, false, &to_account) + ], + &bincode::serialize(&SystemInstruction::CreateAccountWithSeed { + base: from, + seed: seed.to_string(), + lamports: 50, + space: 2, + program_id: new_program_owner + }) + .unwrap() + ), + Ok(()) + ); + 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] + fn test_address_create_with_seed_mismatch() { + let from = Pubkey::new_rand(); + let seed = "dull boy"; + let to = Pubkey::new_rand(); + let program_id = Pubkey::new_rand(); + + assert_eq!( + Address::create(&to, Some((&from, seed, &program_id))), + Err(SystemError::AddressWithSeedMismatch.into()) + ); + } + + #[test] + fn test_create_account_with_seed_missing_sig() { + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + let seed = "dull boy"; + let to = Pubkey::create_with_seed(&from, seed, &new_program_owner).unwrap(); + + 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( + &KeyedAccount::new(&from, false, &from_account), + &mut to_account, + &to_address, + 50, + 2, + &new_program_owner, + &HashSet::new(), + ), + Err(InstructionError::MissingRequiredSignature) + ); + assert_eq!(from_account.borrow().lamports, 100); + assert_eq!(to_account, Account::default()); + } + + #[test] + fn test_create_with_zero_lamports() { + // create account with zero lamports tranferred + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + 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( + &KeyedAccount::new(&from, false, &from_account), // no signer + &mut to_account, + &to.into(), + 0, + 2, + &new_program_owner, + &[to].iter().cloned().collect::>(), + ), + Ok(()) + ); + + 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(); + assert_eq!(from_lamports, 100); + assert_eq!(to_lamports, 0); + assert_eq!(to_owner, new_program_owner); + assert_eq!(to_data, [0, 0]); + } + + #[test] + fn test_create_negative_lamports() { + // 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 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()); + + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut to_account, + &to.into(), + 150, + 2, + &new_program_owner, + &[from, to].iter().cloned().collect::>(), + ); + assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); + } + + #[test] + fn test_request_more_than_allowed_data_length() { + let 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(); + + let signers = &[from, to].iter().cloned().collect::>(); + let address = &to.into(); + + // Trying to request more data length than permitted will result in failure + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut to_account, + &address, + 50, + MAX_PERMITTED_DATA_LENGTH + 1, + &system_program::id(), + &signers, + ); + assert!(result.is_err()); + assert_eq!( + result.err().unwrap(), + SystemError::InvalidAccountDataLength.into() + ); + + // Trying to request equal or less data length than permitted will be successful + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut to_account, + &address, + 50, + MAX_PERMITTED_DATA_LENGTH, + &system_program::id(), + &signers, + ); + assert!(result.is_ok()); + assert_eq!(to_account.lamports, 50); + assert_eq!(to_account.data.len() as u64, MAX_PERMITTED_DATA_LENGTH); + } + + #[test] + fn test_create_already_in_use() { + // 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 from_account = Account::new_ref(100, 0, &system_program::id()); + + let original_program_owner = Pubkey::new(&[5; 32]); + let owned_key = Pubkey::new_rand(); + let mut owned_account = Account::new(0, 0, &original_program_owner); + let unchanged_account = owned_account.clone(); + + let signers = &[from, owned_key].iter().cloned().collect::>(); + let owned_address = owned_key.into(); + + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut owned_account, + &owned_address, + 50, + 2, + &new_program_owner, + &signers, + ); + assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); + + let from_lamports = from_account.borrow().lamports; + assert_eq!(from_lamports, 100); + assert_eq!(owned_account, unchanged_account); + + // Attempt to create system account in account that already has data + let mut owned_account = Account::new(0, 1, &Pubkey::default()); + let unchanged_account = owned_account.clone(); + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut owned_account, + &owned_address, + 50, + 2, + &new_program_owner, + &signers, + ); + assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); + 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( + &KeyedAccount::new(&from, true, &from_account), + &mut owned_account, + &owned_address, + 50, + 2, + &new_program_owner, + &signers, + ); + assert_eq!(result, Ok(())); + assert_eq!(from_account.borrow().lamports, from_lamports - 50); + assert_eq!(owned_account.lamports, 1 + 50); + } + + #[test] + fn test_create_unsigned() { + // Attempt to create an account without signing the transfer + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + let 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()); + + let owned_address = owned_key.into(); + + // Haven't signed from account + let result = create_account( + &KeyedAccount::new(&from, false, &from_account), + &mut owned_account, + &owned_address, + 50, + 2, + &new_program_owner, + &[owned_key].iter().cloned().collect::>(), + ); + assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); + + // Haven't signed to account + let mut owned_account = Account::new(0, 0, &Pubkey::default()); + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut owned_account, + &owned_address, + 50, + 2, + &new_program_owner, + &[from].iter().cloned().collect::>(), + ); + assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); + + // support creation/assignment with zero lamports (ephemeral account) + let mut owned_account = Account::new(0, 0, &Pubkey::default()); + let result = create_account( + &KeyedAccount::new(&from, false, &from_account), + &mut owned_account, + &owned_address, + 0, + 2, + &new_program_owner, + &[owned_key].iter().cloned().collect::>(), + ); + assert_eq!(result, Ok(())); + } + + #[test] + fn test_create_sysvar_invalid_id() { + // Attempt to create system account in account already owned by another program + let from = Pubkey::new_rand(); + let from_account = Account::new_ref(100, 0, &system_program::id()); + + let to = Pubkey::new_rand(); + let mut to_account = Account::default(); + + let signers = [from, to].iter().cloned().collect::>(); + let to_address = to.into(); + + // fail to create a sysvar::id() owned account + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut to_account, + &to_address, + 50, + 2, + &sysvar::id(), + &signers, + ); + + assert_eq!(result, Err(SystemError::InvalidProgramId.into())); + } + + #[test] + fn test_create_data_populated() { + // Attempt to create system account in account with populated data + let new_program_owner = Pubkey::new(&[9; 32]); + let from = Pubkey::new_rand(); + let from_account = Account::new_ref(100, 0, &system_program::id()); + + let populated_key = Pubkey::new_rand(); + let mut populated_account = Account { + data: vec![0, 1, 2, 3], + ..Account::default() + }; + + let signers = [from, populated_key] + .iter() + .cloned() + .collect::>(); + let populated_address = populated_key.into(); + + let result = create_account( + &KeyedAccount::new(&from, true, &from_account), + &mut populated_account, + &populated_address, + 50, + 2, + &new_program_owner, + &signers, + ); + assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); + } + + #[test] + fn test_create_from_account_is_nonce_fail() { + let nonce = Pubkey::new_rand(); + let nonce_account = Account::new_ref_data( + 42, + &nonce::state::Versions::new_current(nonce::State::Initialized( + nonce::state::Data::default(), + )), + &system_program::id(), + ) + .unwrap(); + let from = KeyedAccount::new(&nonce, true, &nonce_account); + let new = Pubkey::new_rand(); + + let mut new_account = Account::default(); + + let signers = [nonce, new].iter().cloned().collect::>(); + let new_address = new.into(); + + assert_eq!( + create_account( + &from, + &mut new_account, + &new_address, + 42, + 0, + &Pubkey::new_rand(), + &signers + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_assign() { + let new_program_owner = Pubkey::new(&[9; 32]); + + let pubkey = Pubkey::new_rand(); + let mut account = Account::new(100, 0, &system_program::id()); + + assert_eq!( + assign( + &mut account, + &pubkey.into(), + &new_program_owner, + &HashSet::new() + ), + Err(InstructionError::MissingRequiredSignature) + ); + // no change, no signature needed + assert_eq!( + assign( + &mut account, + &pubkey.into(), + &system_program::id(), + &HashSet::new() + ), + Ok(()) + ); + + let account = RefCell::new(account); + assert_eq!( + process_instruction( + &Pubkey::default(), + &[KeyedAccount::new(&pubkey, true, &account)], + &bincode::serialize(&SystemInstruction::Assign { + program_id: new_program_owner + }) + .unwrap() + ), + Ok(()) + ); + } + + #[test] + fn test_assign_to_sysvar() { + let new_program_owner = sysvar::id(); + + let from = Pubkey::new_rand(); + let mut from_account = Account::new(100, 0, &system_program::id()); + + assert_eq!( + assign( + &mut from_account, + &from.into(), + &new_program_owner, + &[from].iter().cloned().collect::>(), + ), + Err(SystemError::InvalidProgramId.into()) + ); + } + + #[test] + fn test_process_bogus_instruction() { + // Attempt to assign with no accounts + let instruction = SystemInstruction::Assign { + program_id: Pubkey::new_rand(), + }; + let data = serialize(&instruction).unwrap(); + let result = process_instruction(&system_program::id(), &[], &data); + assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); + + let from = Pubkey::new_rand(); + let 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(); + let result = process_instruction( + &system_program::id(), + &[KeyedAccount::new(&from, true, &from_account)], + &data, + ); + assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); + } + + #[test] + fn test_transfer_lamports() { + let from = Pubkey::new_rand(); + 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 + let from_keyed_account = KeyedAccount::new(&from, true, &from_account); + transfer(&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 from_keyed_account = KeyedAccount::new(&from, true, &from_account); + let result = transfer(&from_keyed_account, &mut to_account, 100); + assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); + assert_eq!(from_keyed_account.account.borrow().lamports, 50); + assert_eq!(to_account.lamports, 51); + + // test unsigned transfer of zero + let from_keyed_account = KeyedAccount::new(&from, false, &from_account); + assert!(transfer(&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 from_account = Account::new_ref_data( + 100, + &nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data { + authority: from, + ..nonce::state::Data::default() + })), + &system_program::id(), + ) + .unwrap(); + assert_eq!( + get_system_account_kind(&from_account.borrow()), + Some(SystemAccountKind::Nonce) + ); + + let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter + assert_eq!( + transfer( + &KeyedAccount::new(&from, true, &from_account), + &mut to_account, + 50, + ), + Err(InstructionError::InvalidArgument), + ) + } + + #[test] + fn test_allocate() { + let (genesis_config, mint_keypair) = create_genesis_config(100); + let bank = Bank::new(&genesis_config); + let bank_client = BankClient::new(bank); + + let alice_keypair = Keypair::new(); + let alice_pubkey = alice_keypair.pubkey(); + let seed = "seed"; + let program_id = Pubkey::new_rand(); + let alice_with_seed = Pubkey::create_with_seed(&alice_pubkey, seed, &program_id).unwrap(); + + bank_client + .transfer(50, &mint_keypair, &alice_pubkey) + .unwrap(); + + let allocate_with_seed = Message::new_with_payer( + &[system_instruction::allocate_with_seed( + &alice_with_seed, + &alice_pubkey, + seed, + 2, + &program_id, + )], + Some(&alice_pubkey), + ); + + assert!(bank_client + .send_message(&[&alice_keypair], allocate_with_seed) + .is_ok()); + + let allocate = system_instruction::allocate(&alice_pubkey, 2); + + assert!(bank_client + .send_instruction(&alice_keypair, allocate) + .is_ok()); + } + + fn with_create_zero_lamport(callback: F) + where + F: Fn(&Bank) -> (), + { + solana_logger::setup(); + + let alice_keypair = Keypair::new(); + let bob_keypair = Keypair::new(); + + let alice_pubkey = alice_keypair.pubkey(); + let bob_pubkey = bob_keypair.pubkey(); + + let program = Pubkey::new_rand(); + let collector = Pubkey::new_rand(); + + let mint_lamports = 10000; + let len1 = 123; + let len2 = 456; + + // create initial bank and fund the alice account + let (genesis_config, mint_keypair) = create_genesis_config(mint_lamports); + let bank = Arc::new(Bank::new(&genesis_config)); + let bank_client = BankClient::new_shared(&bank); + bank_client + .transfer(mint_lamports, &mint_keypair, &alice_pubkey) + .unwrap(); + + // create zero-lamports account to be cleaned + let bank = Arc::new(Bank::new_from_parent(&bank, &collector, bank.slot() + 1)); + let bank_client = BankClient::new_shared(&bank); + let ix = system_instruction::create_account(&alice_pubkey, &bob_pubkey, 0, len1, &program); + let message = Message::new(&[ix]); + let r = bank_client.send_message(&[&alice_keypair, &bob_keypair], message); + assert!(r.is_ok()); + + // transfer some to bogus pubkey just to make previous bank (=slot) really cleanable + let bank = Arc::new(Bank::new_from_parent(&bank, &collector, bank.slot() + 1)); + let bank_client = BankClient::new_shared(&bank); + bank_client + .transfer(50, &alice_keypair, &Pubkey::new_rand()) + .unwrap(); + + // super fun time; callback chooses to .clean_accounts() or not + callback(&*bank); + + // create a normal account at the same pubkey as the zero-lamports account + let bank = Arc::new(Bank::new_from_parent(&bank, &collector, bank.slot() + 1)); + let bank_client = BankClient::new_shared(&bank); + let ix = system_instruction::create_account(&alice_pubkey, &bob_pubkey, 1, len2, &program); + let message = Message::new(&[ix]); + let r = bank_client.send_message(&[&alice_keypair, &bob_keypair], message); + assert!(r.is_ok()); + } + + #[test] + fn test_create_zero_lamport_with_clean() { + with_create_zero_lamport(|bank| { + bank.squash(); + // do clean and assert that it actually did its job + assert_eq!(3, bank.get_snapshot_storages().len()); + bank.clean_accounts(); + assert_eq!(2, bank.get_snapshot_storages().len()); + }); + } + + #[test] + fn test_create_zero_lamport_without_clean() { + with_create_zero_lamport(|_| { + // just do nothing; this should behave identically with test_create_zero_lamport_with_clean + }); + } + + #[test] + fn test_assign_with_seed() { + let (genesis_config, mint_keypair) = create_genesis_config(100); + let bank = Bank::new(&genesis_config); + let bank_client = BankClient::new(bank); + + let alice_keypair = Keypair::new(); + let alice_pubkey = alice_keypair.pubkey(); + let seed = "seed"; + let program_id = Pubkey::new_rand(); + let alice_with_seed = Pubkey::create_with_seed(&alice_pubkey, seed, &program_id).unwrap(); + + bank_client + .transfer(50, &mint_keypair, &alice_pubkey) + .unwrap(); + + let assign_with_seed = Message::new_with_payer( + &[system_instruction::assign_with_seed( + &alice_with_seed, + &alice_pubkey, + seed, + &program_id, + )], + Some(&alice_pubkey), + ); + + assert!(bank_client + .send_message(&[&alice_keypair], assign_with_seed) + .is_ok()); + } + + #[test] + fn test_system_unsigned_transaction() { + let (genesis_config, alice_keypair) = create_genesis_config(100); + let alice_pubkey = alice_keypair.pubkey(); + let mallory_keypair = Keypair::new(); + let mallory_pubkey = mallory_keypair.pubkey(); + + // Fund to account to bypass AccountNotFound error + let bank = Bank::new(&genesis_config); + let bank_client = BankClient::new(bank); + bank_client + .transfer(50, &alice_keypair, &mallory_pubkey) + .unwrap(); + + // Erroneously sign transaction with recipient account key + // No signature case is tested by bank `test_zero_signatures()` + let account_metas = vec![ + AccountMeta::new(alice_pubkey, false), + AccountMeta::new(mallory_pubkey, true), + ]; + let malicious_instruction = Instruction::new( + system_program::id(), + &SystemInstruction::Transfer { lamports: 10 }, + account_metas, + ); + assert_eq!( + bank_client + .send_instruction(&mallory_keypair, malicious_instruction) + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ); + assert_eq!(bank_client.get_balance(&alice_pubkey).unwrap(), 50); + assert_eq!(bank_client.get_balance(&mallory_pubkey).unwrap(), 50); + } + + fn process_nonce_instruction(instruction: &Instruction) -> Result<(), InstructionError> { + let accounts: Vec<_> = instruction + .accounts + .iter() + .map(|meta| { + RefCell::new(if sysvar::recent_blockhashes::check_id(&meta.pubkey) { + create_default_recent_blockhashes_account().into_inner() + } else if sysvar::rent::check_id(&meta.pubkey) { + sysvar::rent::create_account(1, &Rent::free()) + } else { + Account::default() + }) + }) + .collect(); + + { + let keyed_accounts: Vec<_> = instruction + .accounts + .iter() + .zip(accounts.iter()) + .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) + .collect(); + super::process_instruction(&Pubkey::default(), &keyed_accounts, &instruction.data) + } + } + + #[test] + fn test_process_nonce_ix_no_acc_data_fail() { + assert_eq!( + process_nonce_instruction(&system_instruction::advance_nonce_account( + &Pubkey::default(), + &Pubkey::default() + )), + Err(InstructionError::InvalidAccountData), + ); + } + + #[test] + fn test_process_nonce_ix_no_keyed_accs_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[], + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap() + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_nonce_ix_only_nonce_acc_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[KeyedAccount::new( + &Pubkey::default(), + true, + &create_default_account(), + ),], + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_nonce_ix_bad_recent_blockhash_state_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_account(), + ), + ], + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_process_nonce_ix_ok() { + let nonce_acc = nonce::create_account(1_000_000); + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &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![ + IterItem( + 0u64, + &hash(&serialize(&0).unwrap()), + &FeeCalculator::default() + ); + sysvar::recent_blockhashes::MAX_ENTRIES + ] + .into_iter(), + )); + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &new_recent_blockhashes_account, + ), + ], + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + ), + Ok(()), + ); + } + + #[test] + fn test_process_withdraw_ix_no_acc_data_fail() { + assert_eq!( + process_nonce_instruction(&system_instruction::withdraw_nonce_account( + &Pubkey::default(), + &Pubkey::default(), + &Pubkey::default(), + 1, + )), + Err(InstructionError::InvalidAccountData), + ); + } + + #[test] + fn test_process_withdraw_ix_no_keyed_accs_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[], + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_withdraw_ix_only_nonce_acc_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[KeyedAccount::new( + &Pubkey::default(), + true, + &create_default_account() + ),], + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_withdraw_ix_bad_recent_blockhash_state_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &create_default_account()), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_account() + ), + ], + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_process_withdraw_ix_bad_rent_state_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce::create_account(1_000_000),), + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_recent_blockhashes_account(), + ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), + ], + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_process_withdraw_ix_ok() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce::create_account(1_000_000),), + KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_recent_blockhashes_account(), + ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ], + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + ), + Ok(()), + ); + } + + #[test] + fn test_process_initialize_ix_no_keyed_accs_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_initialize_ix_only_nonce_acc_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[KeyedAccount::new( + &Pubkey::default(), + true, + &nonce::create_account(1_000_000), + ),], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + + #[test] + fn test_process_initialize_bad_recent_blockhash_state_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce::create_account(1_000_000),), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_account() + ), + ], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_process_initialize_ix_bad_rent_state_fail() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce::create_account(1_000_000),), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_recent_blockhashes_account(), + ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), + ], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ), + Err(InstructionError::InvalidArgument), + ); + } + + #[test] + fn test_process_initialize_ix_ok() { + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce::create_account(1_000_000),), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_recent_blockhashes_account(), + ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ), + Ok(()), + ); + } + + #[test] + fn test_process_authorize_ix_ok() { + let nonce_acc = nonce::create_account(1_000_000); + super::process_instruction( + &Pubkey::default(), + &[ + KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), + KeyedAccount::new( + &sysvar::recent_blockhashes::id(), + false, + &create_default_recent_blockhashes_account(), + ), + KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ], + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + ) + .unwrap(); + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &[KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,),], + &serialize(&SystemInstruction::AuthorizeNonceAccount(Pubkey::default(),)).unwrap(), + ), + Ok(()), + ); + } + + #[test] + fn test_process_authorize_bad_account_data_fail() { + assert_eq!( + process_nonce_instruction(&system_instruction::authorize_nonce_account( + &Pubkey::default(), + &Pubkey::default(), + &Pubkey::default(), + )), + Err(InstructionError::InvalidAccountData), + ); + } + + #[test] + fn test_get_system_account_kind_system_ok() { + let system_account = Account::default(); + assert_eq!( + get_system_account_kind(&system_account), + Some(SystemAccountKind::System) + ); + } + + #[test] + fn test_get_system_account_kind_nonce_ok() { + let nonce_account = Account::new_data( + 42, + &nonce::state::Versions::new_current(nonce::State::Initialized( + nonce::state::Data::default(), + )), + &system_program::id(), + ) + .unwrap(); + assert_eq!( + get_system_account_kind(&nonce_account), + Some(SystemAccountKind::Nonce) + ); + } + + #[test] + fn test_get_system_account_kind_uninitialized_nonce_account_fail() { + assert_eq!( + get_system_account_kind(&nonce::create_account(42).borrow()), + None + ); + } + + #[test] + fn test_get_system_account_kind_system_owner_nonzero_nonnonce_data_fail() { + let other_data_account = Account::new_data(42, b"other", &Pubkey::default()).unwrap(); + assert_eq!(get_system_account_kind(&other_data_account), None); + } + + #[test] + fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() { + let nonce_account = Account::new_data( + 42, + &nonce::state::Versions::new_current(nonce::State::Initialized( + nonce::state::Data::default(), + )), + &Pubkey::new_rand(), + ) + .unwrap(); + assert_eq!(get_system_account_kind(&nonce_account), None); + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5a9e665d9f0d7b..bb328b2c172ee2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -6,8 +6,10 @@ pub mod bank; pub mod bank_client; mod blockhash_queue; pub mod bloom; +mod builtin_programs; pub mod epoch_stakes; pub mod genesis_utils; +mod legacy_system_instruction_processor0; pub mod loader_utils; pub mod message_processor; mod native_loader;