diff --git a/src/accountant.rs b/src/accountant.rs index 2c00ea124e..43fbb47fce 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -5,7 +5,7 @@ use hash::Hash; use entry::Entry; use event::Event; -use transaction::{Action, Condition, Plan, Transaction}; +use transaction::{Action, Plan, Transaction}; use signature::{KeyPair, PublicKey, Signature}; use mint::Mint; use historian::{reserve_signature, Historian}; @@ -281,6 +281,21 @@ mod tests { ); } + #[test] + fn test_overspend_attack() { + let alice = Mint::new(1); + let mut acc = Accountant::new(&alice, None); + 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! + } + assert_eq!( + acc.process_transaction(tr), + Err(AccountingError::InvalidTransfer) + ); + } + #[test] fn test_transfer_to_newb() { let alice = Mint::new(10_000); diff --git a/src/transaction.rs b/src/transaction.rs index 68a1dcd66a..ce571473e6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -6,6 +6,7 @@ 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 { @@ -18,6 +19,14 @@ pub enum Action { Pay(Payment), } +impl Action { + pub fn max_spendable(&self) -> T { + match *self { + Action::Pay(ref payment) => payment.asset.clone(), + } + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Payment { pub asset: T, @@ -31,7 +40,21 @@ pub enum Plan { Race(Box>, Box>), } -impl Plan { +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(), + } + } + + pub fn verify(&self, spendable_assets: &T) -> bool { + self.max_spendable() <= *spendable_assets + } + pub fn run_race(&mut self) -> bool { let new_plan = if let Plan::Race(ref a, ref b) = *self { if let Plan::Action(_) = **a { @@ -117,7 +140,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 { @@ -180,7 +203,7 @@ impl Transaction { } pub fn verify(&self) -> bool { - self.sig.verify(&self.from, &self.get_sign_data()) + self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(&self.asset) } }