From 3163fbad0e1d4feaad075833c9ac4e7ea22d4523 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Thu, 20 Sep 2018 13:50:11 -0600 Subject: [PATCH] Remove 'Plan' indirection since it's implied by BUDGET_CONTRACT_ID --- src/budget_contract.rs | 53 +++++++++++++++++++++--------------------- src/instruction.rs | 34 ++------------------------- src/transaction.rs | 21 +++++++---------- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/src/budget_contract.rs b/src/budget_contract.rs index e1fd952013..0b20164036 100644 --- a/src/budget_contract.rs +++ b/src/budget_contract.rs @@ -1,8 +1,9 @@ //! budget contract use bank::Account; use bincode::{self, deserialize, serialize_into, serialized_size}; +use budget::Budget; use chrono::prelude::{DateTime, Utc}; -use instruction::{Instruction, Plan}; +use instruction::Instruction; use payment_plan::{PaymentPlan, Witness}; use signature::Pubkey; use std::io; @@ -23,7 +24,7 @@ pub enum BudgetError { #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] pub struct BudgetContract { pub initialized: bool, - pub pending_plan: Option, + pub pending_budget: Option, pub last_error: Option, } @@ -32,7 +33,7 @@ pub const BUDGET_CONTRACT_ID: [u8; 32] = [ ]; impl BudgetContract { fn is_pending(&self) -> bool { - self.pending_plan != None + self.pending_budget != None } pub fn id() -> Pubkey { Pubkey::new(&BUDGET_CONTRACT_ID) @@ -49,9 +50,9 @@ impl BudgetContract { account: &mut [Account], ) -> Result<(), BudgetError> { let mut final_payment = None; - if let Some(ref mut plan) = self.pending_plan { - plan.apply_witness(&Witness::Signature, &keys[0]); - final_payment = plan.final_payment(); + if let Some(ref mut budget) = self.pending_budget { + budget.apply_witness(&Witness::Signature, &keys[0]); + final_payment = budget.final_payment(); } if let Some(payment) = final_payment { @@ -59,7 +60,7 @@ impl BudgetContract { trace!("destination missing"); return Err(BudgetError::DestinationMissing(payment.to)); } - self.pending_plan = None; + self.pending_budget = None; account[1].tokens -= payment.tokens; account[2].tokens += payment.tokens; } @@ -77,9 +78,9 @@ impl BudgetContract { // Check to see if any timelocked transactions can be completed. let mut final_payment = None; - if let Some(ref mut plan) = self.pending_plan { - plan.apply_witness(&Witness::Timestamp(dt), &keys[0]); - final_payment = plan.final_payment(); + if let Some(ref mut budget) = self.pending_budget { + budget.apply_witness(&Witness::Timestamp(dt), &keys[0]); + final_payment = budget.final_payment(); } if let Some(payment) = final_payment { @@ -87,7 +88,7 @@ impl BudgetContract { trace!("destination missing"); return Err(BudgetError::DestinationMissing(payment.to)); } - self.pending_plan = None; + self.pending_budget = None; accounts[1].tokens -= payment.tokens; accounts[2].tokens += payment.tokens; } @@ -133,8 +134,8 @@ impl BudgetContract { ) -> Result<(), BudgetError> { match instruction { Instruction::NewContract(contract) => { - let plan = contract.plan.clone(); - if let Some(payment) = plan.final_payment() { + let budget = contract.budget.clone(); + if let Some(payment) = budget.final_payment() { accounts[1].tokens += payment.tokens; Ok(()) } else { @@ -144,7 +145,7 @@ impl BudgetContract { Err(BudgetError::ContractAlreadyExists(tx.keys[1])) } else { let mut state = BudgetContract::default(); - state.pending_plan = Some(plan); + state.pending_budget = Some(budget); accounts[1].tokens += contract.tokens; state.initialized = true; state.serialize(&mut accounts[1].userdata); @@ -445,9 +446,9 @@ mod test { assert_eq!( tx.userdata, vec![ - 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, - 6, 5, 4, 1, 1, 1 + 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 1, + 1, 1 ] ); @@ -456,16 +457,16 @@ mod test { assert_eq!( tx.userdata, vec![ - 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, - 0, 0, 0, 0, 0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58, - 49, 49, 90, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, - 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 8, 7, 6, 5, 4, 1, 1, 1, 1, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135, + 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, + 0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58, 49, 49, 90, + 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, + 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 8, 7, 6, 5, 4, 1, 1, 1, 1, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, + 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, + 137, 247, 161, 68, 192, 0, 0, 0, 0, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, - 72, 134, 137, 247, 161, 68, 192, 0, 0, 0, 0, 0, 0, 0, 32, 253, 186, 201, 177, 11, - 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, - 252, 180, 72, 134, 137, 247, 161, 68 + 72, 134, 137, 247, 161, 68 ] ); diff --git a/src/instruction.rs b/src/instruction.rs index 002160f6ce..d441c72080 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,42 +1,12 @@ use budget::Budget; use chrono::prelude::{DateTime, Utc}; -use payment_plan::{Payment, PaymentPlan, Witness}; -use signature::Pubkey; - -/// The type of payment plan. Each item must implement the PaymentPlan trait. -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum Plan { - /// The builtin contract language Budget. - Budget(Budget), -} - -// A proxy for the underlying DSL. -impl PaymentPlan for Plan { - fn final_payment(&self) -> Option { - match self { - Plan::Budget(budget) => budget.final_payment(), - } - } - - fn verify(&self, spendable_tokens: i64) -> bool { - match self { - Plan::Budget(budget) => budget.verify(spendable_tokens), - } - } - - fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) { - match self { - Plan::Budget(budget) => budget.apply_witness(witness, from), - } - } -} /// A smart contract. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Contract { - /// The number of tokens allocated to the `Plan` and any transaction fees. + /// The number of tokens allocated to the `Budget` and any transaction fees. pub tokens: i64, - pub plan: Plan, + pub budget: Budget, } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Vote { diff --git a/src/transaction.rs b/src/transaction.rs index 6f289380da..45d75f4947 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -5,7 +5,7 @@ use budget::{Budget, Condition}; use budget_contract::BudgetContract; use chrono::prelude::*; use hash::Hash; -use instruction::{Contract, Instruction, Plan, Vote}; +use instruction::{Contract, Instruction, Vote}; use payment_plan::{Payment, PaymentPlan}; use signature::{Keypair, KeypairUtil, Pubkey, Signature}; use std::mem::size_of; @@ -84,8 +84,7 @@ impl Transaction { to: contract, }; let budget = Budget::Pay(payment); - let plan = Plan::Budget(budget); - let instruction = Instruction::NewContract(Contract { plan, tokens }); + let instruction = Instruction::NewContract(Contract { budget, tokens }); let userdata = serialize(&instruction).unwrap(); Self::new_with_userdata( from_keypair, @@ -168,8 +167,7 @@ impl Transaction { (Condition::Timestamp(dt, from), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ); - let plan = Plan::Budget(budget); - let instruction = Instruction::NewContract(Contract { plan, tokens }); + let instruction = Instruction::NewContract(Contract { budget, tokens }); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new_with_userdata( from_keypair, @@ -296,7 +294,7 @@ impl Transaction { if let Some(Instruction::NewContract(contract)) = self.instruction() { self.fee >= 0 && self.fee <= contract.tokens - && contract.plan.verify(contract.tokens - self.fee) + && contract.budget.verify(contract.tokens - self.fee) } else { true } @@ -363,8 +361,7 @@ mod tests { tokens: 0, to: Default::default(), }); - let plan = Plan::Budget(budget); - let instruction = Instruction::NewContract(Contract { plan, tokens: 0 }); + let instruction = Instruction::NewContract(Contract { budget, tokens: 0 }); let userdata = serialize(&instruction).unwrap(); let claim0 = Transaction { keys: vec![], @@ -388,7 +385,7 @@ mod tests { let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { contract.tokens = 1_000_000; // <-- attack, part 1! - if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { + if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = contract.tokens; // <-- attack, part 2! } } @@ -407,7 +404,7 @@ mod tests { let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero); let mut instruction = tx.instruction(); if let Some(Instruction::NewContract(ref mut contract)) = instruction { - if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { + if let Budget::Pay(ref mut payment) = contract.budget { payment.to = thief_keypair.pubkey(); // <-- attack! } } @@ -461,7 +458,7 @@ mod tests { let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero); let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { - if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { + if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = 2; // <-- attack! } } @@ -471,7 +468,7 @@ mod tests { // Also, ensure all branchs of the plan spend all tokens let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { - if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { + if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = 0; // <-- whoops! } }