Add bool return to entrypoint signature to permit programs to fail transactions
This commit is contained in:
parent
9fc30f6db4
commit
d3b4dfe104
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
17
src/bank.rs
17
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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue