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;
#[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();
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
}
}

View File

@ -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<KeyedAccount>, data: &[u8]) {
pub extern "C" fn process(infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool {
println!("noop: AccountInfos: {:#?}", infos);
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]
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 = str::from_utf8(&code_data).unwrap();
run_lua(keyed_accounts, &code, data).unwrap();
true
}
#[cfg(test)]

View File

@ -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())

View File

@ -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<KeyedAccount>, data: &[u8]);
type Entrypoint = unsafe extern "C" fn(infos: &mut Vec<KeyedAccount>, data: &[u8]) -> bool;
#[derive(Debug)]
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 {
DynamicProgram::Native { name, library } => unsafe {
let entrypoint: Symbol<Entrypoint> = 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
}
}
}

View File

@ -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<Account>, 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"),
}