Cleanup Budget
* BudgetProgram -> BudgetState * Instruction -> BudgetInstruction * Move BudgetState into its own module * BudgetInstruction::NewBudget -> BudgetInstruction::InitializeAccount * BudgetInstruction::new_budget -> BudgetInstruction::new_initialize_account
This commit is contained in:
		@@ -15,20 +15,24 @@ pub struct Contract {
 | 
			
		||||
 | 
			
		||||
/// An instruction to progress the smart contract.
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
 | 
			
		||||
pub enum Instruction {
 | 
			
		||||
pub enum BudgetInstruction {
 | 
			
		||||
    /// Declare and instantiate `BudgetExpr`.
 | 
			
		||||
    NewBudget(BudgetExpr),
 | 
			
		||||
    InitializeAccount(BudgetExpr),
 | 
			
		||||
 | 
			
		||||
    /// Tell a payment plan acknowledge the given `DateTime` has past.
 | 
			
		||||
    ApplyTimestamp(DateTime<Utc>),
 | 
			
		||||
 | 
			
		||||
    /// Tell the budget that the `NewBudget` with `Signature` has been
 | 
			
		||||
    /// Tell the budget that the `InitializeAccount` with `Signature` has been
 | 
			
		||||
    /// signed by the containing transaction's `Pubkey`.
 | 
			
		||||
    ApplySignature,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Instruction {
 | 
			
		||||
    pub fn new_budget(contract: Pubkey, expr: BudgetExpr) -> BuilderInstruction {
 | 
			
		||||
        BuilderInstruction::new(id(), &Instruction::NewBudget(expr), vec![(contract, false)])
 | 
			
		||||
impl BudgetInstruction {
 | 
			
		||||
    pub fn new_initialize_account(contract: Pubkey, expr: BudgetExpr) -> BuilderInstruction {
 | 
			
		||||
        BuilderInstruction::new(
 | 
			
		||||
            id(),
 | 
			
		||||
            &BudgetInstruction::InitializeAccount(expr),
 | 
			
		||||
            vec![(contract, false)],
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
//! The `budget_transaction` module provides functionality for creating Budget transactions.
 | 
			
		||||
 | 
			
		||||
use crate::budget_expr::{BudgetExpr, Condition};
 | 
			
		||||
use crate::budget_instruction::Instruction;
 | 
			
		||||
use crate::budget_instruction::BudgetInstruction;
 | 
			
		||||
use crate::id;
 | 
			
		||||
use bincode::deserialize;
 | 
			
		||||
use chrono::prelude::*;
 | 
			
		||||
@@ -28,7 +28,7 @@ impl BudgetTransaction {
 | 
			
		||||
        let payment = BudgetExpr::new_payment(tokens - fee, to);
 | 
			
		||||
        TransactionBuilder::new(fee)
 | 
			
		||||
            .push(SystemInstruction::new_move(from, contract, tokens))
 | 
			
		||||
            .push(Instruction::new_budget(contract, payment))
 | 
			
		||||
            .push(BudgetInstruction::new_initialize_account(contract, payment))
 | 
			
		||||
            .sign(&[from_keypair], recent_blockhash)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +51,7 @@ impl BudgetTransaction {
 | 
			
		||||
        dt: DateTime<Utc>,
 | 
			
		||||
        recent_blockhash: Hash,
 | 
			
		||||
    ) -> Transaction {
 | 
			
		||||
        let instruction = Instruction::ApplyTimestamp(dt);
 | 
			
		||||
        let instruction = BudgetInstruction::ApplyTimestamp(dt);
 | 
			
		||||
        Transaction::new(
 | 
			
		||||
            from_keypair,
 | 
			
		||||
            &[contract, to],
 | 
			
		||||
@@ -69,7 +69,7 @@ impl BudgetTransaction {
 | 
			
		||||
        to: Pubkey,
 | 
			
		||||
        recent_blockhash: Hash,
 | 
			
		||||
    ) -> Transaction {
 | 
			
		||||
        let instruction = Instruction::ApplySignature;
 | 
			
		||||
        let instruction = BudgetInstruction::ApplySignature;
 | 
			
		||||
        let mut keys = vec![contract];
 | 
			
		||||
        if from_keypair.pubkey() != to {
 | 
			
		||||
            keys.push(to);
 | 
			
		||||
@@ -105,7 +105,7 @@ impl BudgetTransaction {
 | 
			
		||||
                Box::new(BudgetExpr::new_payment(tokens, to)),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        let instruction = Instruction::NewBudget(expr);
 | 
			
		||||
        let instruction = BudgetInstruction::InitializeAccount(expr);
 | 
			
		||||
        Transaction::new(
 | 
			
		||||
            from_keypair,
 | 
			
		||||
            &[contract],
 | 
			
		||||
@@ -142,7 +142,7 @@ impl BudgetTransaction {
 | 
			
		||||
                Box::new(BudgetExpr::new_payment(tokens, to)),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        let instruction = Instruction::NewBudget(expr);
 | 
			
		||||
        let instruction = BudgetInstruction::InitializeAccount(expr);
 | 
			
		||||
        Transaction::new(
 | 
			
		||||
            from_keypair,
 | 
			
		||||
            &[contract],
 | 
			
		||||
@@ -157,14 +157,16 @@ impl BudgetTransaction {
 | 
			
		||||
        deserialize(&tx.userdata(index)).ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn instruction(tx: &Transaction, index: usize) -> Option<Instruction> {
 | 
			
		||||
    pub fn instruction(tx: &Transaction, index: usize) -> Option<BudgetInstruction> {
 | 
			
		||||
        deserialize(&tx.userdata(index)).ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Verify only the payment plan.
 | 
			
		||||
    pub fn verify_plan(tx: &Transaction) -> bool {
 | 
			
		||||
        if let Some(SystemInstruction::Move { tokens }) = Self::system_instruction(tx, 0) {
 | 
			
		||||
            if let Some(Instruction::NewBudget(expr)) = BudgetTransaction::instruction(&tx, 1) {
 | 
			
		||||
            if let Some(BudgetInstruction::InitializeAccount(expr)) =
 | 
			
		||||
                BudgetTransaction::instruction(&tx, 1)
 | 
			
		||||
            {
 | 
			
		||||
                if !(tx.fee <= tokens && expr.verify(tokens - tx.fee)) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
@@ -227,7 +229,7 @@ mod tests {
 | 
			
		||||
        if let SystemInstruction::Move { ref mut tokens } = system_instruction {
 | 
			
		||||
            *tokens = 1_000_000; // <-- attack, part 1!
 | 
			
		||||
            let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
 | 
			
		||||
            if let Instruction::NewBudget(ref mut expr) = instruction {
 | 
			
		||||
            if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
 | 
			
		||||
                if let BudgetExpr::Pay(ref mut payment) = expr {
 | 
			
		||||
                    payment.tokens = *tokens; // <-- attack, part 2!
 | 
			
		||||
                }
 | 
			
		||||
@@ -248,7 +250,7 @@ mod tests {
 | 
			
		||||
        let zero = Hash::default();
 | 
			
		||||
        let mut tx = BudgetTransaction::new(&keypair0, pubkey1, 42, zero);
 | 
			
		||||
        let mut instruction = BudgetTransaction::instruction(&tx, 1);
 | 
			
		||||
        if let Some(Instruction::NewBudget(ref mut expr)) = instruction {
 | 
			
		||||
        if let Some(BudgetInstruction::InitializeAccount(ref mut expr)) = instruction {
 | 
			
		||||
            if let BudgetExpr::Pay(ref mut payment) = expr {
 | 
			
		||||
                payment.to = thief_keypair.pubkey(); // <-- attack!
 | 
			
		||||
            }
 | 
			
		||||
@@ -265,7 +267,7 @@ mod tests {
 | 
			
		||||
        let zero = Hash::default();
 | 
			
		||||
        let mut tx = BudgetTransaction::new(&keypair0, keypair1.pubkey(), 1, zero);
 | 
			
		||||
        let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
 | 
			
		||||
        if let Instruction::NewBudget(ref mut expr) = instruction {
 | 
			
		||||
        if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
 | 
			
		||||
            if let BudgetExpr::Pay(ref mut payment) = expr {
 | 
			
		||||
                payment.tokens = 2; // <-- attack!
 | 
			
		||||
            }
 | 
			
		||||
@@ -275,7 +277,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        // Also, ensure all branchs of the plan spend all tokens
 | 
			
		||||
        let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
 | 
			
		||||
        if let Instruction::NewBudget(ref mut expr) = instruction {
 | 
			
		||||
        if let BudgetInstruction::InitializeAccount(ref mut expr) = instruction {
 | 
			
		||||
            if let BudgetExpr::Pay(ref mut payment) = expr {
 | 
			
		||||
                payment.tokens = 0; // <-- whoops!
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user