2018-09-19 17:25:57 -07:00
|
|
|
//! budget program
|
2019-03-15 15:10:00 -07:00
|
|
|
use bincode::{deserialize, serialize};
|
2018-09-17 13:36:31 -07:00
|
|
|
use chrono::prelude::{DateTime, Utc};
|
2018-12-14 20:39:10 -08:00
|
|
|
use log::*;
|
2019-03-03 13:17:51 -08:00
|
|
|
use solana_budget_api::budget_instruction::BudgetInstruction;
|
2019-03-03 14:43:51 -08:00
|
|
|
use solana_budget_api::budget_state::{BudgetError, BudgetState};
|
2019-03-02 13:23:22 -08:00
|
|
|
use solana_budget_api::payment_plan::Witness;
|
2018-12-04 14:38:19 -08:00
|
|
|
use solana_sdk::account::KeyedAccount;
|
2019-03-15 15:10:00 -07:00
|
|
|
use solana_sdk::native_program::ProgramError;
|
2019-03-07 12:34:13 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-03-03 13:17:51 -08:00
|
|
|
|
|
|
|
/// Process a Witness Signature. Any payment plans waiting on this signature
|
|
|
|
/// will progress one step.
|
|
|
|
fn apply_signature(
|
|
|
|
budget_state: &mut BudgetState,
|
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
|
|
|
) -> Result<(), BudgetError> {
|
|
|
|
let mut final_payment = None;
|
|
|
|
if let Some(ref mut expr) = budget_state.pending_budget {
|
2019-03-15 15:10:00 -07:00
|
|
|
let key = keyed_accounts[0].signer_key().unwrap();
|
2019-03-03 13:17:51 -08:00
|
|
|
expr.apply_witness(&Witness::Signature, key);
|
|
|
|
final_payment = expr.final_payment();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(payment) = final_payment {
|
|
|
|
if let Some(key) = keyed_accounts[0].signer_key() {
|
|
|
|
if &payment.to == key {
|
|
|
|
budget_state.pending_budget = None;
|
2019-03-05 17:27:25 -08:00
|
|
|
keyed_accounts[1].account.lamports -= payment.lamports;
|
|
|
|
keyed_accounts[0].account.lamports += payment.lamports;
|
2019-03-03 13:17:51 -08:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if &payment.to != keyed_accounts[2].unsigned_key() {
|
|
|
|
trace!("destination missing");
|
|
|
|
return Err(BudgetError::DestinationMissing);
|
|
|
|
}
|
|
|
|
budget_state.pending_budget = None;
|
2019-03-05 17:27:25 -08:00
|
|
|
keyed_accounts[1].account.lamports -= payment.lamports;
|
|
|
|
keyed_accounts[2].account.lamports += payment.lamports;
|
2019-03-03 13:17:51 -08:00
|
|
|
}
|
|
|
|
Ok(())
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
|
|
|
|
2019-03-03 13:17:51 -08:00
|
|
|
/// Process a Witness Timestamp. Any payment plans waiting on this timestamp
|
|
|
|
/// will progress one step.
|
|
|
|
fn apply_timestamp(
|
|
|
|
budget_state: &mut BudgetState,
|
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
|
|
|
dt: DateTime<Utc>,
|
|
|
|
) -> Result<(), BudgetError> {
|
|
|
|
// Check to see if any timelocked transactions can be completed.
|
|
|
|
let mut final_payment = None;
|
|
|
|
|
|
|
|
if let Some(ref mut expr) = budget_state.pending_budget {
|
2019-03-15 15:10:00 -07:00
|
|
|
let key = keyed_accounts[0].signer_key().unwrap();
|
2019-03-03 13:17:51 -08:00
|
|
|
expr.apply_witness(&Witness::Timestamp(dt), key);
|
|
|
|
final_payment = expr.final_payment();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(payment) = final_payment {
|
|
|
|
if &payment.to != keyed_accounts[2].unsigned_key() {
|
|
|
|
trace!("destination missing");
|
|
|
|
return Err(BudgetError::DestinationMissing);
|
|
|
|
}
|
|
|
|
budget_state.pending_budget = None;
|
2019-03-05 17:27:25 -08:00
|
|
|
keyed_accounts[1].account.lamports -= payment.lamports;
|
|
|
|
keyed_accounts[2].account.lamports += payment.lamports;
|
2019-03-03 13:17:51 -08:00
|
|
|
}
|
|
|
|
Ok(())
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
|
|
|
|
2019-03-15 15:10:00 -07:00
|
|
|
pub fn process_instruction(
|
|
|
|
_program_id: &Pubkey,
|
2018-12-04 14:38:19 -08:00
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
2019-03-15 15:10:00 -07:00
|
|
|
data: &[u8],
|
2019-03-15 19:48:11 -07:00
|
|
|
_tick_height: u64,
|
2019-03-15 15:10:00 -07:00
|
|
|
) -> Result<(), ProgramError> {
|
|
|
|
let instruction = deserialize(data).map_err(|err| {
|
|
|
|
info!("Invalid transaction data: {:?} {:?}", data, err);
|
|
|
|
ProgramError::InvalidInstructionData
|
|
|
|
})?;
|
|
|
|
|
|
|
|
trace!("process_instruction: {:?}", instruction);
|
|
|
|
|
2018-11-23 12:45:34 -08:00
|
|
|
match instruction {
|
2019-03-03 13:17:51 -08:00
|
|
|
BudgetInstruction::InitializeAccount(expr) => {
|
2018-11-23 12:45:34 -08:00
|
|
|
let expr = expr.clone();
|
|
|
|
if let Some(payment) = expr.final_payment() {
|
2019-03-05 16:28:14 -08:00
|
|
|
keyed_accounts[1].account.lamports = 0;
|
2019-03-05 17:27:25 -08:00
|
|
|
keyed_accounts[0].account.lamports += payment.lamports;
|
2019-03-15 15:10:00 -07:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
let existing = BudgetState::deserialize(&keyed_accounts[0].account.data).ok();
|
|
|
|
if Some(true) == existing.map(|x| x.initialized) {
|
|
|
|
trace!("contract already exists");
|
|
|
|
return Err(ProgramError::AccountAlreadyInitialized);
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
2019-03-15 15:10:00 -07:00
|
|
|
let mut budget_state = BudgetState::default();
|
|
|
|
budget_state.pending_budget = Some(expr);
|
|
|
|
budget_state.initialized = true;
|
|
|
|
budget_state.serialize(&mut keyed_accounts[0].account.data)
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
2019-03-03 13:17:51 -08:00
|
|
|
BudgetInstruction::ApplyTimestamp(dt) => {
|
2019-03-15 15:10:00 -07:00
|
|
|
let mut budget_state = BudgetState::deserialize(&keyed_accounts[1].account.data)?;
|
|
|
|
if !budget_state.is_pending() {
|
|
|
|
return Ok(()); // Nothing to do here.
|
|
|
|
}
|
|
|
|
if !budget_state.initialized {
|
|
|
|
trace!("contract is uninitialized");
|
|
|
|
return Err(ProgramError::UninitializedAccount);
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
2019-03-15 15:10:00 -07:00
|
|
|
if keyed_accounts[0].signer_key().is_none() {
|
|
|
|
return Err(ProgramError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
trace!("apply timestamp");
|
|
|
|
apply_timestamp(&mut budget_state, keyed_accounts, dt)
|
|
|
|
.map_err(|e| ProgramError::CustomError(serialize(&e).unwrap()))?;
|
|
|
|
trace!("apply timestamp committed");
|
|
|
|
budget_state.serialize(&mut keyed_accounts[1].account.data)
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
2019-03-03 13:17:51 -08:00
|
|
|
BudgetInstruction::ApplySignature => {
|
2019-03-15 15:10:00 -07:00
|
|
|
let mut budget_state = BudgetState::deserialize(&keyed_accounts[1].account.data)?;
|
|
|
|
if !budget_state.is_pending() {
|
|
|
|
return Ok(()); // Nothing to do here.
|
|
|
|
}
|
|
|
|
if !budget_state.initialized {
|
|
|
|
trace!("contract is uninitialized");
|
|
|
|
return Err(ProgramError::UninitializedAccount);
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
2019-03-15 15:10:00 -07:00
|
|
|
if keyed_accounts[0].signer_key().is_none() {
|
|
|
|
return Err(ProgramError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
trace!("apply signature");
|
|
|
|
apply_signature(&mut budget_state, keyed_accounts)
|
|
|
|
.map_err(|e| ProgramError::CustomError(serialize(&e).unwrap()))?;
|
|
|
|
trace!("apply signature committed");
|
|
|
|
budget_state.serialize(&mut keyed_accounts[1].account.data)
|
2018-11-23 12:45:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2018-11-23 12:45:34 -08:00
|
|
|
use super::*;
|
2019-03-16 13:30:10 -07:00
|
|
|
use solana_budget_api::budget_instruction::BudgetInstruction;
|
2019-03-18 04:17:59 -07:00
|
|
|
use solana_budget_api::budget_script::BudgetScript;
|
2019-03-02 13:23:22 -08:00
|
|
|
use solana_budget_api::id;
|
2019-03-16 04:50:44 -07:00
|
|
|
use solana_runtime::bank::Bank;
|
2019-03-16 13:30:10 -07:00
|
|
|
use solana_runtime::bank_client::BankClient;
|
2019-03-16 04:50:44 -07:00
|
|
|
use solana_sdk::genesis_block::GenesisBlock;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-03-15 15:10:00 -07:00
|
|
|
use solana_sdk::transaction::{InstructionError, Transaction, TransactionError};
|
2018-09-26 09:33:52 -07:00
|
|
|
|
2019-03-16 04:50:44 -07:00
|
|
|
fn create_bank(lamports: u64) -> (Bank, Keypair) {
|
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(lamports);
|
|
|
|
let mut bank = Bank::new(&genesis_block);
|
2019-03-16 16:20:09 -07:00
|
|
|
bank.add_instruction_processor(id(), process_instruction);
|
2019-03-16 04:50:44 -07:00
|
|
|
(bank, mint_keypair)
|
2018-09-28 16:16:35 -07:00
|
|
|
}
|
2019-03-07 09:35:28 -08:00
|
|
|
|
2019-03-16 16:37:18 -07:00
|
|
|
#[test]
|
|
|
|
fn test_budget_payment() {
|
|
|
|
let (bank, mint_keypair) = create_bank(10_000);
|
|
|
|
let alice_client = BankClient::new(&bank, mint_keypair);
|
|
|
|
let alice_pubkey = alice_client.pubkey();
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
2019-03-18 04:17:59 -07:00
|
|
|
let script = BudgetScript::pay(&alice_pubkey, &bob_pubkey, 100);
|
2019-03-16 16:37:18 -07:00
|
|
|
alice_client.process_script(script).unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&bob_pubkey), 100);
|
|
|
|
}
|
|
|
|
|
2018-10-18 20:10:33 -07:00
|
|
|
#[test]
|
|
|
|
fn test_unsigned_witness_key() {
|
2019-03-16 13:30:10 -07:00
|
|
|
let (bank, mint_keypair) = create_bank(10_000);
|
|
|
|
let alice_client = BankClient::new(&bank, mint_keypair);
|
|
|
|
let alice_pubkey = alice_client.pubkey();
|
2018-10-18 20:10:33 -07:00
|
|
|
|
2019-03-03 13:17:51 -08:00
|
|
|
// Initialize BudgetState
|
2019-03-16 13:30:10 -07:00
|
|
|
let budget_pubkey = Keypair::new().pubkey();
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
2018-10-18 20:10:33 -07:00
|
|
|
let witness = Keypair::new().pubkey();
|
2019-03-18 04:17:59 -07:00
|
|
|
let script = BudgetScript::pay_on_signature(
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
|
|
|
&bob_pubkey,
|
|
|
|
&budget_pubkey,
|
|
|
|
&witness,
|
|
|
|
None,
|
|
|
|
1,
|
|
|
|
);
|
|
|
|
alice_client.process_script(script).unwrap();
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Attack! Part 1: Sign a witness transaction with a random key.
|
2019-03-16 13:30:10 -07:00
|
|
|
let mallory_client = BankClient::new(&bank, Keypair::new());
|
|
|
|
let mallory_pubkey = mallory_client.pubkey();
|
|
|
|
alice_client.transfer(1, &mallory_pubkey).unwrap();
|
|
|
|
let instruction =
|
|
|
|
BudgetInstruction::new_apply_signature(&mallory_pubkey, &budget_pubkey, &bob_pubkey);
|
|
|
|
let mut transaction = Transaction::new(vec![instruction]);
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Attack! Part 2: Point the instruction to the expected, but unsigned, key.
|
2019-03-16 13:30:10 -07:00
|
|
|
transaction.account_keys.push(alice_pubkey);
|
|
|
|
transaction.instructions[0].accounts[0] = 3;
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Ensure the transaction fails because of the unsigned key.
|
|
|
|
assert_eq!(
|
2019-03-16 13:30:10 -07:00
|
|
|
mallory_client.process_transaction(transaction),
|
2019-03-15 15:10:00 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::ProgramError(ProgramError::MissingRequiredSignature)
|
|
|
|
))
|
2018-10-18 20:10:33 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_unsigned_timestamp() {
|
2019-03-16 13:30:10 -07:00
|
|
|
let (bank, mint_keypair) = create_bank(10_000);
|
|
|
|
let alice_client = BankClient::new(&bank, mint_keypair);
|
|
|
|
let alice_pubkey = alice_client.pubkey();
|
2018-10-18 20:10:33 -07:00
|
|
|
|
2019-03-03 13:17:51 -08:00
|
|
|
// Initialize BudgetState
|
2019-03-16 13:30:10 -07:00
|
|
|
let budget_pubkey = Keypair::new().pubkey();
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
2018-10-18 20:10:33 -07:00
|
|
|
let dt = Utc::now();
|
2019-03-18 04:17:59 -07:00
|
|
|
let script = BudgetScript::pay_on_date(
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
|
|
|
&bob_pubkey,
|
|
|
|
&budget_pubkey,
|
2018-10-18 20:10:33 -07:00
|
|
|
dt,
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
2018-10-18 20:10:33 -07:00
|
|
|
None,
|
|
|
|
1,
|
|
|
|
);
|
2019-03-16 13:30:10 -07:00
|
|
|
alice_client.process_script(script).unwrap();
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Attack! Part 1: Sign a timestamp transaction with a random key.
|
2019-03-16 13:30:10 -07:00
|
|
|
let mallory_client = BankClient::new(&bank, Keypair::new());
|
|
|
|
let mallory_pubkey = mallory_client.pubkey();
|
|
|
|
alice_client.transfer(1, &mallory_pubkey).unwrap();
|
|
|
|
let instruction = BudgetInstruction::new_apply_timestamp(
|
|
|
|
&mallory_pubkey,
|
|
|
|
&budget_pubkey,
|
|
|
|
&bob_pubkey,
|
|
|
|
dt,
|
|
|
|
);
|
|
|
|
let mut transaction = Transaction::new(vec![instruction]);
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Attack! Part 2: Point the instruction to the expected, but unsigned, key.
|
2019-03-16 13:30:10 -07:00
|
|
|
transaction.account_keys.push(alice_pubkey);
|
|
|
|
transaction.instructions[0].accounts[0] = 3;
|
2018-10-18 20:10:33 -07:00
|
|
|
|
|
|
|
// Ensure the transaction fails because of the unsigned key.
|
|
|
|
assert_eq!(
|
2019-03-16 13:30:10 -07:00
|
|
|
mallory_client.process_transaction(transaction),
|
2019-03-15 15:10:00 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::ProgramError(ProgramError::MissingRequiredSignature)
|
|
|
|
))
|
2018-10-18 20:10:33 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
#[test]
|
2019-03-18 04:17:59 -07:00
|
|
|
fn test_pay_on_date() {
|
2019-03-16 13:30:10 -07:00
|
|
|
let (bank, mint_keypair) = create_bank(2);
|
|
|
|
let alice_client = BankClient::new(&bank, mint_keypair);
|
|
|
|
let alice_pubkey = alice_client.pubkey();
|
|
|
|
let budget_pubkey = Keypair::new().pubkey();
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let mallory_pubkey = Keypair::new().pubkey();
|
2018-09-17 13:36:31 -07:00
|
|
|
let dt = Utc::now();
|
2019-03-18 04:17:59 -07:00
|
|
|
let script = BudgetScript::pay_on_date(
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
|
|
|
&bob_pubkey,
|
|
|
|
&budget_pubkey,
|
2018-09-17 13:36:31 -07:00
|
|
|
dt,
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
2018-09-22 16:51:21 -07:00
|
|
|
None,
|
2018-09-17 13:36:31 -07:00
|
|
|
1,
|
|
|
|
);
|
2019-03-16 13:30:10 -07:00
|
|
|
alice_client.process_script(script).unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_balance(&budget_pubkey), 1);
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
let contract_account = bank.get_account(&budget_pubkey).unwrap();
|
2019-03-16 04:50:44 -07:00
|
|
|
let budget_state = BudgetState::deserialize(&contract_account.data).unwrap();
|
2019-03-03 13:17:51 -08:00
|
|
|
assert!(budget_state.is_pending());
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
// Attack! Try to payout to mallory_pubkey
|
|
|
|
let instruction = BudgetInstruction::new_apply_timestamp(
|
|
|
|
&alice_pubkey,
|
|
|
|
&budget_pubkey,
|
|
|
|
&mallory_pubkey,
|
2018-09-17 13:36:31 -07:00
|
|
|
dt,
|
|
|
|
);
|
2018-09-24 16:00:55 -07:00
|
|
|
assert_eq!(
|
2019-03-17 08:55:42 -07:00
|
|
|
alice_client.process_instruction(instruction).unwrap_err(),
|
2019-03-15 15:10:00 -07:00
|
|
|
TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::ProgramError(ProgramError::CustomError(
|
|
|
|
serialize(&BudgetError::DestinationMissing).unwrap()
|
|
|
|
))
|
|
|
|
)
|
2018-09-24 16:00:55 -07:00
|
|
|
);
|
2019-03-16 13:30:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_balance(&budget_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_balance(&bob_pubkey), 0);
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
let contract_account = bank.get_account(&budget_pubkey).unwrap();
|
2019-03-16 04:50:44 -07:00
|
|
|
let budget_state = BudgetState::deserialize(&contract_account.data).unwrap();
|
2019-03-03 13:17:51 -08:00
|
|
|
assert!(budget_state.is_pending());
|
2018-09-17 13:36:31 -07:00
|
|
|
|
|
|
|
// Now, acknowledge the time in the condition occurred and
|
|
|
|
// that pubkey's funds are now available.
|
2019-03-16 13:30:10 -07:00
|
|
|
let instruction =
|
|
|
|
BudgetInstruction::new_apply_timestamp(&alice_pubkey, &budget_pubkey, &bob_pubkey, dt);
|
2019-03-17 08:55:42 -07:00
|
|
|
alice_client.process_instruction(instruction).unwrap();
|
2019-03-16 13:30:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_balance(&budget_pubkey), 0);
|
|
|
|
assert_eq!(bank.get_balance(&bob_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_account(&budget_pubkey), None);
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
#[test]
|
2019-03-18 04:17:59 -07:00
|
|
|
fn test_cancel_payment() {
|
2019-03-16 13:30:10 -07:00
|
|
|
let (bank, mint_keypair) = create_bank(3);
|
|
|
|
let alice_client = BankClient::new(&bank, mint_keypair);
|
|
|
|
let alice_pubkey = alice_client.pubkey();
|
|
|
|
let budget_pubkey = Keypair::new().pubkey();
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
2018-09-17 13:36:31 -07:00
|
|
|
let dt = Utc::now();
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2019-03-18 04:17:59 -07:00
|
|
|
let script = BudgetScript::pay_on_date(
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
|
|
|
&bob_pubkey,
|
|
|
|
&budget_pubkey,
|
2018-09-17 13:36:31 -07:00
|
|
|
dt,
|
2019-03-16 13:30:10 -07:00
|
|
|
&alice_pubkey,
|
|
|
|
Some(alice_pubkey),
|
2018-09-17 13:36:31 -07:00
|
|
|
1,
|
|
|
|
);
|
2019-03-16 13:30:10 -07:00
|
|
|
alice_client.process_script(script).unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 2);
|
|
|
|
assert_eq!(bank.get_balance(&budget_pubkey), 1);
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2019-03-16 13:30:10 -07:00
|
|
|
let contract_account = bank.get_account(&budget_pubkey).unwrap();
|
2019-03-16 04:50:44 -07:00
|
|
|
let budget_state = BudgetState::deserialize(&contract_account.data).unwrap();
|
2019-03-03 13:17:51 -08:00
|
|
|
assert!(budget_state.is_pending());
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
// Attack! try to put the lamports into the wrong account with cancel
|
2019-03-16 13:30:10 -07:00
|
|
|
let mallory_client = BankClient::new(&bank, Keypair::new());
|
|
|
|
let mallory_pubkey = mallory_client.pubkey();
|
|
|
|
alice_client.transfer(1, &mallory_pubkey).unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 1);
|
|
|
|
|
|
|
|
let instruction =
|
|
|
|
BudgetInstruction::new_apply_signature(&mallory_pubkey, &budget_pubkey, &bob_pubkey);
|
2019-03-17 08:55:42 -07:00
|
|
|
mallory_client.process_instruction(instruction).unwrap();
|
2018-09-17 13:36:31 -07:00
|
|
|
// nothing should be changed because apply witness didn't finalize a payment
|
2019-03-16 13:30:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_balance(&budget_pubkey), 1);
|
|
|
|
assert_eq!(bank.get_account(&bob_pubkey), None);
|
|
|
|
|
|
|
|
// Now, cancel the transaction. mint gets her funds back
|
|
|
|
let instruction =
|
|
|
|
BudgetInstruction::new_apply_signature(&alice_pubkey, &budget_pubkey, &alice_pubkey);
|
2019-03-17 08:55:42 -07:00
|
|
|
alice_client.process_instruction(instruction).unwrap();
|
2019-03-16 13:30:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&alice_pubkey), 2);
|
|
|
|
assert_eq!(bank.get_account(&budget_pubkey), None);
|
|
|
|
assert_eq!(bank.get_account(&bob_pubkey), None);
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
|
|
|
}
|