From d3b4dfe104316704b3610b240d2722ee7a8aa36a Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 13 Oct 2018 19:31:16 -0700 Subject: [PATCH] Add bool return to entrypoint signature to permit programs to fail transactions --- programs/native/move_funds/src/lib.rs | 4 +++- programs/native/noop/src/lib.rs | 3 ++- programs/native/solua/src/lib.rs | 3 ++- src/bank.rs | 17 ++++++++++------- src/dynamic_program.rs | 7 ++++--- tests/programs.rs | 18 +++++++++--------- 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/programs/native/move_funds/src/lib.rs b/programs/native/move_funds/src/lib.rs index 9f66f24fc0..1f7c9dd256 100644 --- a/programs/native/move_funds/src/lib.rs +++ b/programs/native/move_funds/src/lib.rs @@ -5,16 +5,18 @@ use bincode::deserialize; use solana_program_interface::account::KeyedAccount; #[no_mangle] -pub extern "C" fn process(infos: &mut Vec, data: &[u8]) { +pub extern "C" fn process(infos: &mut Vec, data: &[u8]) -> bool { let tokens: i64 = deserialize(data).unwrap(); if infos[0].account.tokens >= tokens { infos[0].account.tokens -= tokens; infos[1].account.tokens += tokens; + true } else { println!( "Insufficient funds, asked {}, only had {}", tokens, infos[0].account.tokens ); + false } } diff --git a/programs/native/noop/src/lib.rs b/programs/native/noop/src/lib.rs index 3e83449044..4d06acbbe2 100644 --- a/programs/native/noop/src/lib.rs +++ b/programs/native/noop/src/lib.rs @@ -3,7 +3,8 @@ extern crate solana_program_interface; use solana_program_interface::account::KeyedAccount; #[no_mangle] -pub extern "C" fn process(infos: &mut Vec, data: &[u8]) { +pub extern "C" fn process(infos: &mut Vec, data: &[u8]) -> bool { println!("noop: AccountInfos: {:#?}", infos); println!("noop: data: {:#?}", data); + true } diff --git a/programs/native/solua/src/lib.rs b/programs/native/solua/src/lib.rs index 3dab099741..13b8a33750 100644 --- a/programs/native/solua/src/lib.rs +++ b/programs/native/solua/src/lib.rs @@ -45,10 +45,11 @@ fn run_lua(keyed_accounts: &mut Vec, code: &str, data: &[u8]) -> R } #[no_mangle] -pub extern "C" fn process(keyed_accounts: &mut Vec, data: &[u8]) { +pub extern "C" fn process(keyed_accounts: &mut Vec, data: &[u8]) -> bool { let code_data = keyed_accounts[0].account.userdata.clone(); let code = str::from_utf8(&code_data).unwrap(); run_lua(keyed_accounts, &code, data).unwrap(); + true } #[cfg(test)] diff --git a/src/bank.rs b/src/bank.rs index 5d52490e74..42b50ca531 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -520,9 +520,9 @@ impl Bank { &self, tx_program_id: &Pubkey, tx: &Transaction, - program_index: usize, + instruction_index: usize, accounts: &mut [&mut Account], - ) -> bool { + ) -> Result<()> { let loaded_contracts = self.loaded_contracts.write().unwrap(); match loaded_contracts.get(&tx_program_id) { Some(dc) => { @@ -532,10 +532,13 @@ impl Bank { .map(|(key, account)| KeyedAccount { key, account }) .collect(); - dc.call(&mut infos, tx.userdata(program_index)); - true + if dc.call(&mut infos, tx.userdata(instruction_index)) { + Ok(()) + } else { + Err(BankError::ProgramRuntimeError(instruction_index as u8)) + } } - None => false, + None => Err(BankError::UnknownContractId(instruction_index as u8)), } } @@ -628,10 +631,10 @@ impl Bank { { return Err(BankError::ProgramRuntimeError(instruction_index as u8)); } - } else if self.loaded_contract(tx_program_id, tx, instruction_index, program_accounts) { } else { - return Err(BankError::UnknownContractId(instruction_index as u8)); + self.loaded_contract(tx_program_id, tx, instruction_index, program_accounts)?; } + // Verify the transaction for ((pre_program_id, pre_tokens), post_account) in pre_data.iter().zip(program_accounts.iter()) diff --git a/src/dynamic_program.rs b/src/dynamic_program.rs index ebf7435265..4b3a5abc53 100644 --- a/src/dynamic_program.rs +++ b/src/dynamic_program.rs @@ -64,7 +64,7 @@ impl ProgramPath { // All programs export a symbol named process() const ENTRYPOINT: &str = "process"; -type Entrypoint = unsafe extern "C" fn(infos: &mut Vec, data: &[u8]); +type Entrypoint = unsafe extern "C" fn(infos: &mut Vec, data: &[u8]) -> bool; #[derive(Debug)] pub enum DynamicProgram { @@ -168,7 +168,7 @@ impl DynamicProgram { } } - pub fn call(&self, infos: &mut Vec, data: &[u8]) { + pub fn call(&self, infos: &mut Vec, data: &[u8]) -> bool { match self { DynamicProgram::Native { name, library } => unsafe { let entrypoint: Symbol = match library.get(ENTRYPOINT.as_bytes()) { @@ -178,7 +178,7 @@ impl DynamicProgram { e, ENTRYPOINT, name ), }; - entrypoint(infos, data); + entrypoint(infos, data) }, DynamicProgram::Bpf { prog, .. } => { println!("Instructions: {}", prog.len() / 8); @@ -195,6 +195,7 @@ impl DynamicProgram { let mut v = DynamicProgram::serialize(infos, data); vm.prog_exec(v.as_mut_slice()); DynamicProgram::deserialize(infos, &v); + true // TODO: return false on Bpf program failure } } } diff --git a/tests/programs.rs b/tests/programs.rs index ae841e22b8..2777c0473f 100644 --- a/tests/programs.rs +++ b/tests/programs.rs @@ -51,7 +51,7 @@ fn test_bpf_file_noop_rust() { .collect(); let dp = DynamicProgram::new_bpf_from_file("noop_rust".to_string()); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } } @@ -73,7 +73,7 @@ fn test_bpf_file_move_funds_c() { .collect(); let dp = DynamicProgram::new_bpf_from_file("move_funds_c".to_string()); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } } @@ -97,7 +97,7 @@ fn tictactoe_command(command: Command, accounts: &mut Vec, player: Pubk .collect(); let dp = DynamicProgram::new_bpf_from_file("tictactoe_c".to_string()); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } } @@ -143,7 +143,7 @@ fn test_native_file_noop() { .collect(); let dp = DynamicProgram::new_native("noop".to_string()).unwrap(); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } } @@ -164,7 +164,7 @@ fn test_native_file_move_funds_success() { .collect(); let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } assert_eq!(0, accounts[0].tokens); assert_eq!(101, accounts[1].tokens); @@ -187,7 +187,7 @@ fn test_native_file_move_funds_insufficient_funds() { .collect(); let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); - dp.call(&mut infos, &data); + assert!(!dp.call(&mut infos, &data)); } assert_eq!(10, accounts[0].tokens); assert_eq!(1, accounts[1].tokens); @@ -217,7 +217,7 @@ fn test_program_native_move_funds_succes_many_threads() { .collect(); let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } assert_eq!(0, accounts[0].tokens); assert_eq!(101, accounts[1].tokens); @@ -276,7 +276,7 @@ fn test_system_program_load_call() { .map(|(key, account)| KeyedAccount { key, account }) .collect(); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } None => panic!("failed to find program in hash"), } @@ -329,7 +329,7 @@ fn test_system_program_load_call_many_threads() { .map(|(key, account)| KeyedAccount { key, account }) .collect(); - dp.call(&mut infos, &data); + assert!(dp.call(&mut infos, &data)); } None => panic!("failed to find program in hash"), }