Add bool return to entrypoint signature to permit programs to fail transactions

This commit is contained in:
Michael Vines 2018-10-13 19:31:16 -07:00
parent 9fc30f6db4
commit d3b4dfe104
6 changed files with 30 additions and 22 deletions

View File

@ -5,16 +5,18 @@ use bincode::deserialize;
use solana_program_interface::account::KeyedAccount; use solana_program_interface::account::KeyedAccount;
#[no_mangle] #[no_mangle]
pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) { pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool {
let tokens: i64 = deserialize(data).unwrap(); let tokens: i64 = deserialize(data).unwrap();
if infos[0].account.tokens >= tokens { if infos[0].account.tokens >= tokens {
infos[0].account.tokens -= tokens; infos[0].account.tokens -= tokens;
infos[1].account.tokens += tokens; infos[1].account.tokens += tokens;
true
} else { } else {
println!( println!(
"Insufficient funds, asked {}, only had {}", "Insufficient funds, asked {}, only had {}",
tokens, infos[0].account.tokens tokens, infos[0].account.tokens
); );
false
} }
} }

View File

@ -3,7 +3,8 @@ extern crate solana_program_interface;
use solana_program_interface::account::KeyedAccount; use solana_program_interface::account::KeyedAccount;
#[no_mangle] #[no_mangle]
pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) { pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool {
println!("noop: AccountInfos: {:#?}", infos); println!("noop: AccountInfos: {:#?}", infos);
println!("noop: data: {:#?}", data); println!("noop: data: {:#?}", data);
true
} }

View File

@ -45,10 +45,11 @@ fn run_lua(keyed_accounts: &mut Vec<KeyedAccount>, code: &str, data: &[u8]) -> R
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn process(keyed_accounts: &mut Vec<KeyedAccount>, data: &[u8]) { pub extern "C" fn process(keyed_accounts: &mut Vec<KeyedAccount>, data: &[u8]) -> bool {
let code_data = keyed_accounts[0].account.userdata.clone(); let code_data = keyed_accounts[0].account.userdata.clone();
let code = str::from_utf8(&code_data).unwrap(); let code = str::from_utf8(&code_data).unwrap();
run_lua(keyed_accounts, &code, data).unwrap(); run_lua(keyed_accounts, &code, data).unwrap();
true
} }
#[cfg(test)] #[cfg(test)]

View File

@ -520,9 +520,9 @@ impl Bank {
&self, &self,
tx_program_id: &Pubkey, tx_program_id: &Pubkey,
tx: &Transaction, tx: &Transaction,
program_index: usize, instruction_index: usize,
accounts: &mut [&mut Account], accounts: &mut [&mut Account],
) -> bool { ) -> Result<()> {
let loaded_contracts = self.loaded_contracts.write().unwrap(); let loaded_contracts = self.loaded_contracts.write().unwrap();
match loaded_contracts.get(&tx_program_id) { match loaded_contracts.get(&tx_program_id) {
Some(dc) => { Some(dc) => {
@ -532,10 +532,13 @@ impl Bank {
.map(|(key, account)| KeyedAccount { key, account }) .map(|(key, account)| KeyedAccount { key, account })
.collect(); .collect();
dc.call(&mut infos, tx.userdata(program_index)); if dc.call(&mut infos, tx.userdata(instruction_index)) {
true 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)); return Err(BankError::ProgramRuntimeError(instruction_index as u8));
} }
} else if self.loaded_contract(tx_program_id, tx, instruction_index, program_accounts) {
} else { } else {
return Err(BankError::UnknownContractId(instruction_index as u8)); self.loaded_contract(tx_program_id, tx, instruction_index, program_accounts)?;
} }
// Verify the transaction // Verify the transaction
for ((pre_program_id, pre_tokens), post_account) in for ((pre_program_id, pre_tokens), post_account) in
pre_data.iter().zip(program_accounts.iter()) pre_data.iter().zip(program_accounts.iter())

View File

@ -64,7 +64,7 @@ impl ProgramPath {
// All programs export a symbol named process() // All programs export a symbol named process()
const ENTRYPOINT: &str = "process"; const ENTRYPOINT: &str = "process";
type Entrypoint = unsafe extern "C" fn(infos: &mut Vec<KeyedAccount>, data: &[u8]); type Entrypoint = unsafe extern "C" fn(infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool;
#[derive(Debug)] #[derive(Debug)]
pub enum DynamicProgram { pub enum DynamicProgram {
@ -168,7 +168,7 @@ impl DynamicProgram {
} }
} }
pub fn call(&self, infos: &mut Vec<KeyedAccount>, data: &[u8]) { pub fn call(&self, infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool {
match self { match self {
DynamicProgram::Native { name, library } => unsafe { DynamicProgram::Native { name, library } => unsafe {
let entrypoint: Symbol<Entrypoint> = match library.get(ENTRYPOINT.as_bytes()) { let entrypoint: Symbol<Entrypoint> = match library.get(ENTRYPOINT.as_bytes()) {
@ -178,7 +178,7 @@ impl DynamicProgram {
e, ENTRYPOINT, name e, ENTRYPOINT, name
), ),
}; };
entrypoint(infos, data); entrypoint(infos, data)
}, },
DynamicProgram::Bpf { prog, .. } => { DynamicProgram::Bpf { prog, .. } => {
println!("Instructions: {}", prog.len() / 8); println!("Instructions: {}", prog.len() / 8);
@ -195,6 +195,7 @@ impl DynamicProgram {
let mut v = DynamicProgram::serialize(infos, data); let mut v = DynamicProgram::serialize(infos, data);
vm.prog_exec(v.as_mut_slice()); vm.prog_exec(v.as_mut_slice());
DynamicProgram::deserialize(infos, &v); DynamicProgram::deserialize(infos, &v);
true // TODO: return false on Bpf program failure
} }
} }
} }

View File

@ -51,7 +51,7 @@ fn test_bpf_file_noop_rust() {
.collect(); .collect();
let dp = DynamicProgram::new_bpf_from_file("noop_rust".to_string()); 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(); .collect();
let dp = DynamicProgram::new_bpf_from_file("move_funds_c".to_string()); 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<Account>, player: Pubk
.collect(); .collect();
let dp = DynamicProgram::new_bpf_from_file("tictactoe_c".to_string()); 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(); .collect();
let dp = DynamicProgram::new_native("noop".to_string()).unwrap(); 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(); .collect();
let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); 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!(0, accounts[0].tokens);
assert_eq!(101, accounts[1].tokens); assert_eq!(101, accounts[1].tokens);
@ -187,7 +187,7 @@ fn test_native_file_move_funds_insufficient_funds() {
.collect(); .collect();
let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); 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!(10, accounts[0].tokens);
assert_eq!(1, accounts[1].tokens); assert_eq!(1, accounts[1].tokens);
@ -217,7 +217,7 @@ fn test_program_native_move_funds_succes_many_threads() {
.collect(); .collect();
let dp = DynamicProgram::new_native("move_funds".to_string()).unwrap(); 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!(0, accounts[0].tokens);
assert_eq!(101, accounts[1].tokens); assert_eq!(101, accounts[1].tokens);
@ -276,7 +276,7 @@ fn test_system_program_load_call() {
.map(|(key, account)| KeyedAccount { key, account }) .map(|(key, account)| KeyedAccount { key, account })
.collect(); .collect();
dp.call(&mut infos, &data); assert!(dp.call(&mut infos, &data));
} }
None => panic!("failed to find program in hash"), 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 }) .map(|(key, account)| KeyedAccount { key, account })
.collect(); .collect();
dp.call(&mut infos, &data); assert!(dp.call(&mut infos, &data));
} }
None => panic!("failed to find program in hash"), None => panic!("failed to find program in hash"),
} }