Move spending endpoints into expressions

This commit is contained in:
Greg Fitzgerald 2018-03-10 17:11:12 -07:00
parent 7a0bc7d888
commit 8c40d1bd72
3 changed files with 84 additions and 29 deletions

View File

@ -109,12 +109,13 @@ impl Accountant {
/// Commit funds to the 'to' party. /// Commit funds to the 'to' party.
fn complete_transaction(self: &mut Self, tr: &Transaction<i64>) { fn complete_transaction(self: &mut Self, tr: &Transaction<i64>) {
if self.balances.contains_key(&tr.plan.to) { let to = tr.plan.to();
if let Some(x) = self.balances.get_mut(&tr.plan.to) { if self.balances.contains_key(&to) {
if let Some(x) = self.balances.get_mut(&to) {
*x += tr.asset; *x += tr.asset;
} }
} else { } else {
self.balances.insert(tr.plan.to, tr.asset); self.balances.insert(to, tr.asset);
} }
} }
@ -149,17 +150,17 @@ impl Accountant {
return Err(AccountingError::InvalidTransferSignature); return Err(AccountingError::InvalidTransferSignature);
} }
if !tr.plan.unless_any.is_empty() { if !tr.plan.unless_any.0.is_empty() {
// TODO: Check to see if the transaction is expired. // TODO: Check to see if the transaction is expired.
} }
if !Self::is_deposit(allow_deposits, &tr.from, &tr.plan.to) { if !Self::is_deposit(allow_deposits, &tr.from, &tr.plan.to()) {
if let Some(x) = self.balances.get_mut(&tr.from) { if let Some(x) = self.balances.get_mut(&tr.from) {
*x -= tr.asset; *x -= tr.asset;
} }
} }
if !self.all_satisfied(&tr.plan.if_all) { if !self.all_satisfied(&tr.plan.if_all.0) {
self.pending.insert(tr.sig, tr.clone()); self.pending.insert(tr.sig, tr.clone());
return Ok(()); return Ok(());
} }
@ -175,7 +176,7 @@ impl Accountant {
// if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map. // if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map.
// TODO: Use find(). // TODO: Use find().
for cond in &tr.plan.unless_any { for cond in &tr.plan.unless_any.0 {
if let Condition::Signature(pubkey) = *cond { if let Condition::Signature(pubkey) = *cond {
if from == pubkey { if from == pubkey {
cancel = true; cancel = true;
@ -220,17 +221,17 @@ impl Accountant {
// Check to see if any timelocked transactions can be completed. // Check to see if any timelocked transactions can be completed.
let mut completed = vec![]; let mut completed = vec![];
for (key, tr) in &self.pending { for (key, tr) in &self.pending {
for cond in &tr.plan.if_all { for cond in &tr.plan.if_all.0 {
if let Condition::Timestamp(dt) = *cond { if let Condition::Timestamp(dt) = *cond {
if self.last_time >= dt { if self.last_time >= dt {
if tr.plan.if_all.len() == 1 { if tr.plan.if_all.0.len() == 1 {
completed.push(*key); completed.push(*key);
} }
} }
} }
} }
// TODO: Add this in once we start removing constraints // TODO: Add this in once we start removing constraints
//if tr.plan.if_all.is_empty() { //if tr.plan.if_all.0.is_empty() {
// // TODO: Remove tr from pending // // TODO: Remove tr from pending
// self.complete_transaction(tr); // self.complete_transaction(tr);
//} //}

View File

@ -63,7 +63,7 @@ mod tests {
fn test_create_events() { fn test_create_events() {
let mut events = Mint::new(100).create_events().into_iter(); let mut events = Mint::new(100).create_events().into_iter();
if let Event::Transaction(tr) = events.next().unwrap() { if let Event::Transaction(tr) = events.next().unwrap() {
assert_eq!(tr.from, tr.plan.to); assert_eq!(tr.from, tr.plan.to());
} }
assert_eq!(events.next(), None); assert_eq!(events.next(), None);
} }

View File

@ -13,30 +13,59 @@ pub enum Condition {
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct SpendingPlan { pub enum Action<T> {
Pay(Payment<T>),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Payment<T> {
pub asset: T,
pub to: PublicKey, pub to: PublicKey,
pub if_all: Vec<Condition>, }
pub unless_any: Vec<Condition>,
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct SpendingPlan<T> {
pub if_all: (Vec<Condition>, Action<T>),
pub unless_any: (Vec<Condition>, Action<T>),
}
impl<T> SpendingPlan<T> {
pub fn to(&self) -> PublicKey {
let Action::Pay(ref payment) = self.if_all.1;
payment.to
}
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Transaction<T> { pub struct Transaction<T> {
pub from: PublicKey, pub from: PublicKey,
pub plan: SpendingPlan, pub plan: SpendingPlan<T>,
pub asset: T, pub asset: T,
pub last_id: Hash, pub last_id: Hash,
pub sig: Signature, pub sig: Signature,
} }
impl<T: Serialize> Transaction<T> { impl<T: Serialize + Clone> Transaction<T> {
pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self {
let from = from_keypair.pubkey();
let plan = SpendingPlan { let plan = SpendingPlan {
to, if_all: (
if_all: vec![], vec![],
unless_any: vec![], Action::Pay(Payment {
asset: asset.clone(),
to,
}),
),
unless_any: (
vec![],
Action::Pay(Payment {
asset: asset.clone(),
to: from,
}),
),
}; };
let mut tr = Transaction { let mut tr = Transaction {
from: from_keypair.pubkey(), from,
plan, plan,
asset, asset,
last_id, last_id,
@ -55,9 +84,20 @@ impl<T: Serialize> Transaction<T> {
) -> Self { ) -> Self {
let from = from_keypair.pubkey(); let from = from_keypair.pubkey();
let plan = SpendingPlan { let plan = SpendingPlan {
to, if_all: (
if_all: vec![Condition::Timestamp(dt)], vec![Condition::Timestamp(dt)],
unless_any: vec![Condition::Signature(from)], Action::Pay(Payment {
asset: asset.clone(),
to,
}),
),
unless_any: (
vec![Condition::Signature(from)],
Action::Pay(Payment {
asset: asset.clone(),
to: from,
}),
),
}; };
let mut tr = Transaction { let mut tr = Transaction {
from, from,
@ -74,7 +114,6 @@ impl<T: Serialize> Transaction<T> {
let plan = &self.plan; let plan = &self.plan;
serialize(&( serialize(&(
&self.from, &self.from,
&plan.to,
&plan.if_all, &plan.if_all,
&plan.unless_any, &plan.unless_any,
&self.asset, &self.asset,
@ -121,9 +160,20 @@ mod tests {
#[test] #[test]
fn test_serialize_claim() { fn test_serialize_claim() {
let plan = SpendingPlan { let plan = SpendingPlan {
to: Default::default(), if_all: (
if_all: Default::default(), Default::default(),
unless_any: Default::default(), Action::Pay(Payment {
asset: 0,
to: Default::default(),
}),
),
unless_any: (
Default::default(),
Action::Pay(Payment {
asset: 0,
to: Default::default(),
}),
),
}; };
let claim0 = Transaction { let claim0 = Transaction {
from: Default::default(), from: Default::default(),
@ -155,9 +205,13 @@ mod tests {
let thief_keypair = KeyPair::new(); let thief_keypair = KeyPair::new();
let pubkey1 = keypair1.pubkey(); let pubkey1 = keypair1.pubkey();
let zero = Hash::default(); let zero = Hash::default();
let mut tr = Transaction::new(&keypair0, pubkey1, hash(b"hello, world"), zero); let asset = hash(b"hello, world");
let mut tr = Transaction::new(&keypair0, pubkey1, asset, zero);
tr.sign(&keypair0); tr.sign(&keypair0);
tr.plan.to = thief_keypair.pubkey(); // <-- attack! tr.plan.if_all.1 = Action::Pay(Payment {
asset,
to: thief_keypair.pubkey(),
}); // <-- attack!
assert!(!tr.verify()); assert!(!tr.verify());
} }
} }