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;
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
17
src/bank.rs
17
src/bank.rs
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue