diff --git a/src/system_program.rs b/src/system_program.rs index a503ff4510..133e585657 100644 --- a/src/system_program.rs +++ b/src/system_program.rs @@ -36,64 +36,80 @@ pub fn id() -> Pubkey { pub fn get_balance(account: &Account) -> u64 { account.tokens } -pub fn process_instruction( - tx: &Transaction, - pix: usize, - accounts: &mut [&mut Account], -) -> Result<()> { + +fn process_instruction(tx: &Transaction, pix: usize, accounts: &mut [&mut Account]) -> Result<()> { if let Ok(syscall) = deserialize(tx.userdata(pix)) { trace!("process_instruction: {:?}", syscall); + let from = 0; + + // all system instructions require that accounts_keys[0] be a signer + if tx.signed_key(pix, from).is_none() { + Err(Error::InvalidArgument)?; + } + match syscall { SystemInstruction::CreateAccount { tokens, space, program_id, } => { - if !check_id(&accounts[0].owner) { - info!("Invalid account[0] owner"); + let to = 1; + + if !check_id(&accounts[from].owner) { + info!("CreateAccount: invalid account[from] owner"); Err(Error::InvalidArgument)?; } - if space > 0 && (!accounts[1].userdata.is_empty() || !check_id(&accounts[1].owner)) + if space > 0 + && (!accounts[to].userdata.is_empty() || !check_id(&accounts[to].owner)) { - info!("Invalid account[1]"); + info!( + "CreateAccount: invalid argument space: {} accounts.userdata.len(): {}", + space, + accounts[to].userdata.len(), + ); Err(Error::InvalidArgument)?; } - if tokens > accounts[0].tokens { + if tokens > accounts[from].tokens { info!( - "Insufficient tokens in account[0] ({} > {})", - tokens, accounts[0].tokens + "CreateAccount: insufficient tokens ({}, need {})", + accounts[from].tokens, tokens ); Err(Error::ResultWithNegativeTokens)?; } - accounts[0].tokens -= tokens; - accounts[1].tokens += tokens; - accounts[1].owner = program_id; - accounts[1].userdata = vec![0; space as usize]; - accounts[1].executable = false; - accounts[1].loader = Pubkey::default(); + accounts[from].tokens -= tokens; + accounts[to].tokens += tokens; + accounts[to].owner = program_id; + accounts[to].userdata = vec![0; space as usize]; + accounts[to].executable = false; + accounts[to].loader = Pubkey::default(); } SystemInstruction::Assign { program_id } => { - if !check_id(&accounts[0].owner) { + if !check_id(&accounts[from].owner) { Err(Error::AssignOfUnownedAccount)?; } - accounts[0].owner = program_id; + accounts[from].owner = program_id; } SystemInstruction::Move { tokens } => { + let to = 1; + //bank should be verifying correctness - if tokens > accounts[0].tokens { - info!("Insufficient tokens in account[0]"); + if tokens > accounts[from].tokens { + info!( + "Move: insufficient tokens ({}, need {})", + accounts[from].tokens, tokens + ); Err(Error::ResultWithNegativeTokens)?; } - accounts[0].tokens -= tokens; - accounts[1].tokens += tokens; + accounts[from].tokens -= tokens; + accounts[to].tokens += tokens; } SystemInstruction::Spawn => { - if !accounts[0].executable || accounts[0].loader != Pubkey::default() { + if !accounts[from].executable || accounts[from].loader != Pubkey::default() { Err(Error::AccountNotFinalized)?; } - accounts[0].loader = accounts[0].owner; - accounts[0].owner = tx.account_keys[0]; + accounts[from].loader = accounts[from].owner; + accounts[from].owner = tx.account_keys[from]; } } Ok(()) @@ -124,9 +140,30 @@ mod test { use system_transaction::SystemTransaction; use transaction::Transaction; + /// Execute a function with a subset of accounts as writable references. + /// Since the subset can point to the same references, in any order there is no way + /// for the borrow checker to track them with regards to the original set. + fn with_subset(accounts: &mut [Account], ixes: &[u8], func: F) -> A + where + F: FnOnce(&mut [&mut Account]) -> A, + { + let mut subset: Vec<&mut Account> = ixes + .iter() + .map(|ix| { + let ptr = &mut accounts[*ix as usize] as *mut Account; + // lifetime of this unsafe is only within the scope of the closure + // there is no way to reorder them without breaking borrow checker rules + unsafe { &mut *ptr } + }).collect(); + func(&mut subset) + } fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> { - let mut refs: Vec<&mut Account> = accounts.iter_mut().collect(); - super::process_instruction(&tx, 0, &mut refs[..]) + for (instruction_index, instruction) in tx.instructions.iter().enumerate() { + with_subset(accounts, &instruction.accounts, |mut program_accounts| { + super::process_instruction(tx, instruction_index, &mut program_accounts) + })?; + } + Ok(()) } #[test] @@ -251,7 +288,21 @@ mod test { assert_eq!(accounts[0].tokens, 0); assert_eq!(accounts[1].tokens, 1); } - /// Detect binary changes in the serialized program userdata, which could have a downstream + + #[test] + fn test_move_many() { + let from = Keypair::new(); + let tos = [(Keypair::new().pubkey(), 2), (Keypair::new().pubkey(), 1)]; + let mut accounts = vec![Account::default(), Account::default(), Account::default()]; + accounts[0].tokens = 3; + let tx = Transaction::system_move_many(&from, &tos, Hash::default(), 0); + process_transaction(&tx, &mut accounts).unwrap(); + assert_eq!(accounts[0].tokens, 0); + assert_eq!(accounts[1].tokens, 2); + assert_eq!(accounts[2].tokens, 1); + } + + // Detect binary changes in the serialized program userdata, which could have a downstream /// affect on SDKs and DApps #[test] fn test_sdk_serialize() {