diff --git a/src/accountant.rs b/src/accountant.rs index 43fbb47fc..72b03eab9 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -288,10 +288,19 @@ mod tests { let bob_pubkey = KeyPair::new().pubkey(); let mut tr = Transaction::new(&alice.keypair(), bob_pubkey, 1, alice.seed()); if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { - payment.asset = 2; // <-- Attack! + payment.asset = 2; // <-- attack! } assert_eq!( - acc.process_transaction(tr), + acc.process_transaction(tr.clone()), + Err(AccountingError::InvalidTransfer) + ); + + // Also, ensure all branchs of the plan spend all assets + if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { + payment.asset = 0; // <-- whoops! + } + assert_eq!( + acc.process_transaction(tr.clone()), Err(AccountingError::InvalidTransfer) ); } diff --git a/src/transaction.rs b/src/transaction.rs index ce571473e..9cf7a68f6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -6,7 +6,6 @@ use bincode::serialize; use hash::Hash; use chrono::prelude::*; use std::mem; -use std::cmp; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Condition { @@ -20,7 +19,7 @@ pub enum Action { } impl Action { - pub fn max_spendable(&self) -> T { + pub fn spendable(&self) -> T { match *self { Action::Pay(ref payment) => payment.asset.clone(), } @@ -40,19 +39,15 @@ pub enum Plan { Race(Box>, Box>), } -impl Plan { - pub fn max_spendable(&self) -> T { - match *self { - Plan::Action(ref action) => action.max_spendable(), - Plan::Race(ref plan_a, ref plan_b) => { - cmp::max(plan_a.max_spendable(), plan_b.max_spendable()) - } - Plan::After(_, ref action) => action.max_spendable(), - } - } - +impl Plan { pub fn verify(&self, spendable_assets: &T) -> bool { - self.max_spendable() <= *spendable_assets + match *self { + Plan::Action(ref action) => action.spendable() == *spendable_assets, + Plan::Race(ref plan_a, ref plan_b) => { + plan_a.verify(spendable_assets) && plan_b.verify(spendable_assets) + } + Plan::After(_, ref action) => action.spendable() == *spendable_assets, + } } pub fn run_race(&mut self) -> bool { @@ -140,7 +135,7 @@ pub struct Transaction { pub sig: Signature, } -impl Transaction { +impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { let from = from_keypair.pubkey(); let plan = Plan::Action(Action::Pay(Payment {