Avoid panicking invalid instructions

This commit is contained in:
Michael Vines 2018-09-20 13:54:42 -07:00 committed by Grimes
parent 5691bf557c
commit a6c15684c9
3 changed files with 65 additions and 38 deletions

View File

@ -246,14 +246,17 @@ impl BudgetContract {
/// * accounts[1] - The contract context. Once the contract has been completed, the tokens can
/// be spent from this account .
pub fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> () {
let instruction = deserialize(&tx.userdata).unwrap();
trace!("process_transaction: {:?}", instruction);
let _ = Self::apply_debits_to_budget_state(tx, accounts, &instruction)
.and_then(|_| Self::apply_credits_to_budget_state(tx, accounts, &instruction))
.map_err(|e| {
trace!("saving error {:?}", e);
Self::save_error_to_budget_state(e, accounts);
});
if let Ok(instruction) = deserialize(&tx.userdata) {
trace!("process_transaction: {:?}", instruction);
let _ = Self::apply_debits_to_budget_state(tx, accounts, &instruction)
.and_then(|_| Self::apply_credits_to_budget_state(tx, accounts, &instruction))
.map_err(|e| {
trace!("saving error {:?}", e);
Self::save_error_to_budget_state(e, accounts);
});
} else {
info!("Invalid transaction userdata: {:?}", tx.userdata);
}
}
//TODO the contract needs to provide a "get_balance" introspection call of the userdata
@ -299,6 +302,27 @@ mod test {
Err(BudgetError::UserdataTooSmall)
);
}
#[test]
fn test_invalid_instruction() {
let mut accounts = vec![
Account::new(1, 0, BudgetContract::id()),
Account::new(0, 512, BudgetContract::id()),
];
let from = Keypair::new();
let contract = Keypair::new();
let tx = Transaction::new_with_userdata(
&from,
&[contract.pubkey()],
BudgetContract::id(),
vec![1, 2, 3], // <== garbage instruction
Hash::default(),
0,
);
BudgetContract::process_transaction(&tx, &mut accounts);
// Success if there was no panic...
}
#[test]
fn test_transfer_on_date() {

View File

@ -41,39 +41,42 @@ impl SystemContract {
account.tokens
}
pub fn process_transaction(tx: &Transaction, accounts: &mut [Account]) {
let syscall: SystemContract = deserialize(&tx.userdata).unwrap();
trace!("process_transaction: {:?}", syscall);
match syscall {
SystemContract::CreateAccount {
tokens,
space,
contract_id,
} => {
if !Self::check_id(&accounts[0].contract_id) {
return;
if let Ok(syscall) = deserialize(&tx.userdata) {
trace!("process_transaction: {:?}", syscall);
match syscall {
SystemContract::CreateAccount {
tokens,
space,
contract_id,
} => {
if !Self::check_id(&accounts[0].contract_id) {
return;
}
if space > 0
&& (!accounts[1].userdata.is_empty()
|| !Self::check_id(&accounts[1].contract_id))
{
return;
}
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
accounts[1].contract_id = contract_id;
accounts[1].userdata = vec![0; space as usize];
}
if space > 0
&& (!accounts[1].userdata.is_empty()
|| !Self::check_id(&accounts[1].contract_id))
{
return;
SystemContract::Assign { contract_id } => {
if !Self::check_id(&accounts[0].contract_id) {
return;
}
accounts[0].contract_id = contract_id;
}
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
accounts[1].contract_id = contract_id;
accounts[1].userdata = vec![0; space as usize];
}
SystemContract::Assign { contract_id } => {
if !Self::check_id(&accounts[0].contract_id) {
return;
SystemContract::Move { tokens } => {
//bank should be verifying correctness
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
}
accounts[0].contract_id = contract_id;
}
SystemContract::Move { tokens } => {
//bank should be verifying correctness
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
}
} else {
info!("Invalid transaction userdata: {:?}", tx.userdata);
}
}
}

View File

@ -49,7 +49,7 @@ impl Transaction {
/// * `userdata` - The input data that the contract will execute with
/// * `last_id` - The PoH hash.
/// * `fee` - The transaction fee.
fn new_with_userdata(
pub fn new_with_userdata(
from_keypair: &Keypair,
transaction_keys: &[Pubkey],
contract_id: Pubkey,