diff --git a/src/bank.rs b/src/bank.rs index bc2c6813b..a8ff8a96e 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -5,7 +5,6 @@ extern crate libc; -use budget::Budget; use chrono::prelude::*; use entry::Entry; use hash::Hash; @@ -18,7 +17,7 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::result; use std::sync::RwLock; use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering}; -use transaction::{Instruction, Transaction}; +use transaction::{Instruction, Plan, Transaction}; pub const MAX_ENTRY_IDS: usize = 1024 * 4; @@ -33,7 +32,7 @@ pub type Result = result::Result; pub struct Bank { balances: RwLock>, - pending: RwLock>, + pending: RwLock>, last_ids: RwLock>)>>, time_sources: RwLock>, last_time: RwLock>, diff --git a/src/mint.rs b/src/mint.rs index b8c811b7f..46c5a0315 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -70,14 +70,14 @@ mod tests { use super::*; use budget::Budget; use ledger::Block; - use transaction::Instruction; + use transaction::{Instruction, Plan}; #[test] fn test_create_transactions() { let mut transactions = Mint::new(100).create_transactions().into_iter(); let tx = transactions.next().unwrap(); if let Instruction::NewContract(contract) = tx.instruction { - if let Budget::Pay(payment) = contract.plan { + if let Plan::Budget(Budget::Pay(payment)) = contract.plan { assert_eq!(tx.from, payment.to); } } diff --git a/src/thin_client.rs b/src/thin_client.rs index 1b9af5f1d..86e4585f6 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -187,7 +187,7 @@ mod tests { use std::sync::atomic::{AtomicBool, Ordering}; use std::thread::sleep; use std::time::Duration; - use transaction::Instruction; + use transaction::{Instruction, Plan}; use tvu::TestNode; #[test] @@ -282,7 +282,7 @@ mod tests { let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id); if let Instruction::NewContract(contract) = &mut tr2.instruction { contract.tokens = 502; - contract.plan = Budget::new_payment(502, bob_pubkey); + contract.plan = Plan::Budget(Budget::new_payment(502, bob_pubkey)); } let _sig = client.transfer_signed(tr2).unwrap(); diff --git a/src/transaction.rs b/src/transaction.rs index 6eb278e71..20f1d9c16 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -4,17 +4,43 @@ use bincode::serialize; use budget::{Budget, Condition}; use chrono::prelude::*; use hash::Hash; -use plan::{Payment, PaymentPlan}; +use plan::{Payment, PaymentPlan, Witness}; use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; pub const SIGNED_DATA_OFFSET: usize = 112; pub const SIG_OFFSET: usize = 8; pub const PUB_KEY_OFFSET: usize = 80; +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum Plan { + 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) { + match self { + Plan::Budget(budget) => budget.apply_witness(witness), + } + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Contract { pub tokens: i64, - pub plan: Budget, + pub plan: Plan, } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] @@ -51,7 +77,8 @@ impl Transaction { /// Create and sign a new Transaction. Used for unit-testing. pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { - let plan = Budget::Pay(Payment { tokens, to }); + let budget = Budget::Pay(Payment { tokens, to }); + let plan = Plan::Budget(budget); let instruction = Instruction::NewContract(Contract { plan, tokens }); Self::new_from_instruction(from_keypair, instruction, last_id) } @@ -77,10 +104,11 @@ impl Transaction { last_id: Hash, ) -> Self { let from = from_keypair.pubkey(); - let plan = Budget::Race( + let budget = Budget::Race( (Condition::Timestamp(dt), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ); + let plan = Plan::Budget(budget); let instruction = Instruction::NewContract(Contract { plan, tokens }); let mut tx = Transaction { instruction, @@ -163,10 +191,11 @@ mod tests { #[test] fn test_serialize_claim() { - let plan = Budget::Pay(Payment { + let budget = Budget::Pay(Payment { tokens: 0, to: Default::default(), }); + let plan = Plan::Budget(budget); let instruction = Instruction::NewContract(Contract { plan, tokens: 0 }); let claim0 = Transaction { instruction, @@ -187,7 +216,7 @@ mod tests { let mut tx = Transaction::new(&keypair, pubkey, 42, zero); if let Instruction::NewContract(contract) = &mut tx.instruction { contract.tokens = 1_000_000; // <-- attack, part 1! - if let Budget::Pay(ref mut payment) = contract.plan { + if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { payment.tokens = contract.tokens; // <-- attack, part 2! } } @@ -204,7 +233,7 @@ mod tests { let zero = Hash::default(); let mut tx = Transaction::new(&keypair0, pubkey1, 42, zero); if let Instruction::NewContract(contract) = &mut tx.instruction { - if let Budget::Pay(ref mut payment) = contract.plan { + if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { payment.to = thief_keypair.pubkey(); // <-- attack! } } @@ -228,7 +257,7 @@ mod tests { let zero = Hash::default(); let mut tx = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero); if let Instruction::NewContract(contract) = &mut tx.instruction { - if let Budget::Pay(ref mut payment) = contract.plan { + if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { payment.tokens = 2; // <-- attack! } } @@ -236,7 +265,7 @@ mod tests { // Also, ensure all branchs of the plan spend all tokens if let Instruction::NewContract(contract) = &mut tx.instruction { - if let Budget::Pay(ref mut payment) = contract.plan { + if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan { payment.tokens = 0; // <-- whoops! } }