Ensure the server isn't passed a Plan that spends more than is bonded

This commit is contained in:
Greg Fitzgerald 2018-03-11 11:53:45 -06:00
parent 0eb3669fbf
commit aa0a184ebe
2 changed files with 42 additions and 4 deletions

View File

@ -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);

View File

@ -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<T> {
Pay(Payment<T>),
}
impl<T: Clone> Action<T> {
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<T> {
pub asset: T,
@ -31,7 +40,21 @@ pub enum Plan<T> {
Race(Box<Plan<T>>, Box<Plan<T>>),
}
impl<T: Clone> Plan<T> {
impl<T: Clone + Ord> Plan<T> {
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<T> {
pub sig: Signature,
}
impl<T: Serialize + Clone> Transaction<T> {
impl<T: Serialize + Clone + Ord> Transaction<T> {
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<T: Serialize + Clone> Transaction<T> {
}
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)
}
}