diff --git a/src/budget.rs b/src/budget_expr.rs similarity index 66% rename from src/budget.rs rename to src/budget_expr.rs index 76d3e14b9d..f411211cd1 100644 --- a/src/budget.rs +++ b/src/budget_expr.rs @@ -1,4 +1,4 @@ -//! The `budget` module provides a domain-specific language for payment plans. Users create Budget objects that +//! The `budget_expr` module provides a domain-specific language for payment plans. Users create BudgetExpr objects that //! are given to an interpreter. The interpreter listens for `Witness` transactions, //! which it uses to reduce the payment plan. When the budget is reduced to a //! `Payment`, the payment is executed. @@ -34,7 +34,7 @@ impl Condition { /// A data type representing a payment plan. #[repr(C)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum Budget { +pub enum BudgetExpr { /// Make a payment. Pay(Payment), @@ -49,20 +49,20 @@ pub enum Budget { And(Condition, Condition, Payment), } -impl Budget { +impl BudgetExpr { /// Create the simplest budget - one that pays `tokens` to Pubkey. pub fn new_payment(tokens: i64, to: Pubkey) -> Self { - Budget::Pay(Payment { tokens, to }) + BudgetExpr::Pay(Payment { tokens, to }) } /// Create a budget that pays `tokens` to `to` after being witnessed by `from`. pub fn new_authorized_payment(from: Pubkey, tokens: i64, to: Pubkey) -> Self { - Budget::After(Condition::Signature(from), Payment { tokens, to }) + BudgetExpr::After(Condition::Signature(from), Payment { tokens, to }) } /// Create a budget that pays tokens` to `to` after being witnessed by 2x `from`s pub fn new_2_2_multisig_payment(from0: Pubkey, from1: Pubkey, tokens: i64, to: Pubkey) -> Self { - Budget::And( + BudgetExpr::And( Condition::Signature(from0), Condition::Signature(from1), Payment { tokens, to }, @@ -71,7 +71,7 @@ impl Budget { /// Create a budget that pays `tokens` to `to` after the given DateTime. pub fn new_future_payment(dt: DateTime, from: Pubkey, tokens: i64, to: Pubkey) -> Self { - Budget::After(Condition::Timestamp(dt, from), Payment { tokens, to }) + BudgetExpr::After(Condition::Timestamp(dt, from), Payment { tokens, to }) } /// Create a budget that pays `tokens` to `to` after the given DateTime @@ -82,7 +82,7 @@ impl Budget { tokens: i64, to: Pubkey, ) -> Self { - Budget::Or( + BudgetExpr::Or( (Condition::Timestamp(dt, from), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ) @@ -91,7 +91,7 @@ impl Budget { /// Return Payment if the budget requires no additional Witnesses. pub fn final_payment(&self) -> Option { match self { - Budget::Pay(payment) => Some(payment.clone()), + BudgetExpr::Pay(payment) => Some(payment.clone()), _ => None, } } @@ -99,39 +99,39 @@ impl Budget { /// Return true if the budget spends exactly `spendable_tokens`. pub fn verify(&self, spendable_tokens: i64) -> bool { match self { - Budget::Pay(payment) | Budget::After(_, payment) | Budget::And(_, _, payment) => { + BudgetExpr::Pay(payment) | BudgetExpr::After(_, payment) | BudgetExpr::And(_, _, payment) => { payment.tokens == spendable_tokens } - Budget::Or(a, b) => a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens, + BudgetExpr::Or(a, b) => a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens, } } /// Apply a witness to the budget to see if the budget can be reduced. /// If so, modify the budget in-place. pub fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) { - let new_budget = match self { - Budget::After(cond, payment) if cond.is_satisfied(witness, from) => { - Some(Budget::Pay(payment.clone())) + let new_expr = match self { + BudgetExpr::After(cond, payment) if cond.is_satisfied(witness, from) => { + Some(BudgetExpr::Pay(payment.clone())) } - Budget::Or((cond, payment), _) if cond.is_satisfied(witness, from) => { - Some(Budget::Pay(payment.clone())) + BudgetExpr::Or((cond, payment), _) if cond.is_satisfied(witness, from) => { + Some(BudgetExpr::Pay(payment.clone())) } - Budget::Or(_, (cond, payment)) if cond.is_satisfied(witness, from) => { - Some(Budget::Pay(payment.clone())) + BudgetExpr::Or(_, (cond, payment)) if cond.is_satisfied(witness, from) => { + Some(BudgetExpr::Pay(payment.clone())) } - Budget::And(cond0, cond1, payment) => { + BudgetExpr::And(cond0, cond1, payment) => { if cond0.is_satisfied(witness, from) { - Some(Budget::After(cond1.clone(), payment.clone())) + Some(BudgetExpr::After(cond1.clone(), payment.clone())) } else if cond1.is_satisfied(witness, from) { - Some(Budget::After(cond0.clone(), payment.clone())) + Some(BudgetExpr::After(cond0.clone(), payment.clone())) } else { None } } _ => None, }; - if let Some(budget) = new_budget { - mem::replace(self, budget); + if let Some(expr) = new_expr { + mem::replace(self, expr); } } } @@ -162,10 +162,10 @@ mod tests { let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); let from = Pubkey::default(); let to = Pubkey::default(); - assert!(Budget::new_payment(42, to).verify(42)); - assert!(Budget::new_authorized_payment(from, 42, to).verify(42)); - assert!(Budget::new_future_payment(dt, from, 42, to).verify(42)); - assert!(Budget::new_cancelable_future_payment(dt, from, 42, to).verify(42)); + assert!(BudgetExpr::new_payment(42, to).verify(42)); + assert!(BudgetExpr::new_authorized_payment(from, 42, to).verify(42)); + assert!(BudgetExpr::new_future_payment(dt, from, 42, to).verify(42)); + assert!(BudgetExpr::new_cancelable_future_payment(dt, from, 42, to).verify(42)); } #[test] @@ -173,9 +173,9 @@ mod tests { let from = Pubkey::default(); let to = Pubkey::default(); - let mut budget = Budget::new_authorized_payment(from, 42, to); - budget.apply_witness(&Witness::Signature, &from); - assert_eq!(budget, Budget::new_payment(42, to)); + let mut expr = BudgetExpr::new_authorized_payment(from, 42, to); + expr.apply_witness(&Witness::Signature, &from); + assert_eq!(expr, BudgetExpr::new_payment(42, to)); } #[test] @@ -184,9 +184,9 @@ mod tests { let from = Keypair::new().pubkey(); let to = Keypair::new().pubkey(); - let mut budget = Budget::new_future_payment(dt, from, 42, to); - budget.apply_witness(&Witness::Timestamp(dt), &from); - assert_eq!(budget, Budget::new_payment(42, to)); + let mut expr = BudgetExpr::new_future_payment(dt, from, 42, to); + expr.apply_witness(&Witness::Timestamp(dt), &from); + assert_eq!(expr, BudgetExpr::new_payment(42, to)); } #[test] @@ -197,10 +197,10 @@ mod tests { let from = Keypair::new().pubkey(); let to = Keypair::new().pubkey(); - let mut budget = Budget::new_future_payment(dt, from, 42, to); - let orig_budget = budget.clone(); - budget.apply_witness(&Witness::Timestamp(dt), &to); // <-- Attack! - assert_eq!(budget, orig_budget); + let mut expr = BudgetExpr::new_future_payment(dt, from, 42, to); + let orig_expr = expr.clone(); + expr.apply_witness(&Witness::Timestamp(dt), &to); // <-- Attack! + assert_eq!(expr, orig_expr); } #[test] @@ -209,13 +209,13 @@ mod tests { let from = Pubkey::default(); let to = Pubkey::default(); - let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to); - budget.apply_witness(&Witness::Timestamp(dt), &from); - assert_eq!(budget, Budget::new_payment(42, to)); + let mut expr = BudgetExpr::new_cancelable_future_payment(dt, from, 42, to); + expr.apply_witness(&Witness::Timestamp(dt), &from); + assert_eq!(expr, BudgetExpr::new_payment(42, to)); - let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to); - budget.apply_witness(&Witness::Signature, &from); - assert_eq!(budget, Budget::new_payment(42, from)); + let mut expr = BudgetExpr::new_cancelable_future_payment(dt, from, 42, to); + expr.apply_witness(&Witness::Signature, &from); + assert_eq!(expr, BudgetExpr::new_payment(42, from)); } #[test] fn test_2_2_multisig_payment() { @@ -223,8 +223,8 @@ mod tests { let from1 = Keypair::new().pubkey(); let to = Pubkey::default(); - let mut budget = Budget::new_2_2_multisig_payment(from0, from1, 42, to); - budget.apply_witness(&Witness::Signature, &from0); - assert_eq!(budget, Budget::new_authorized_payment(from1, 42, to)); + let mut expr = BudgetExpr::new_2_2_multisig_payment(from0, from1, 42, to); + expr.apply_witness(&Witness::Signature, &from0); + assert_eq!(expr, BudgetExpr::new_authorized_payment(from1, 42, to)); } } diff --git a/src/budget_instruction.rs b/src/budget_instruction.rs index c7e83a2cff..d80d5d2e94 100644 --- a/src/budget_instruction.rs +++ b/src/budget_instruction.rs @@ -1,19 +1,19 @@ -use budget::Budget; +use budget_expr::BudgetExpr; use chrono::prelude::{DateTime, Utc}; /// A smart contract. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Contract { - /// The number of tokens allocated to the `Budget` and any transaction fees. + /// The number of tokens allocated to the `BudgetExpr` and any transaction fees. pub tokens: i64, - pub budget: Budget, + pub budget_expr: BudgetExpr, } /// An instruction to progress the smart contract. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Instruction { - /// Declare and instantiate `Budget`. - NewBudget(Budget), + /// Declare and instantiate `BudgetExpr`. + NewBudget(BudgetExpr), /// Tell a payment plan acknowledge the given `DateTime` has past. ApplyTimestamp(DateTime), diff --git a/src/budget_program.rs b/src/budget_program.rs index bff3adeba2..8975f2f02b 100644 --- a/src/budget_program.rs +++ b/src/budget_program.rs @@ -1,6 +1,6 @@ //! budget program use bincode::{self, deserialize, serialize_into, serialized_size}; -use budget::Budget; +use budget_expr::BudgetExpr; use budget_instruction::Instruction; use chrono::prelude::{DateTime, Utc}; use payment_plan::Witness; @@ -27,7 +27,7 @@ pub enum BudgetError { #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] pub struct BudgetState { pub initialized: bool, - pub pending_budget: Option, + pub pending_budget: Option, } const BUDGET_PROGRAM_ID: [u8; 32] = [ @@ -55,13 +55,13 @@ impl BudgetState { accounts: &mut [&mut Account], ) -> Result<(), BudgetError> { let mut final_payment = None; - if let Some(ref mut budget) = self.pending_budget { + if let Some(ref mut expr) = self.pending_budget { let key = match tx.signed_key(instruction_index, 0) { None => return Err(BudgetError::UnsignedKey), Some(key) => key, }; - budget.apply_witness(&Witness::Signature, key); - final_payment = budget.final_payment(); + expr.apply_witness(&Witness::Signature, key); + final_payment = expr.final_payment(); } if let Some(payment) = final_payment { @@ -88,13 +88,13 @@ impl BudgetState { // Check to see if any timelocked transactions can be completed. let mut final_payment = None; - if let Some(ref mut budget) = self.pending_budget { + if let Some(ref mut expr) = self.pending_budget { let key = match tx.signed_key(instruction_index, 0) { None => return Err(BudgetError::UnsignedKey), Some(key) => key, }; - budget.apply_witness(&Witness::Timestamp(dt), key); - final_payment = budget.final_payment(); + expr.apply_witness(&Witness::Timestamp(dt), key); + final_payment = expr.final_payment(); } if let Some(payment) = final_payment { @@ -120,9 +120,9 @@ impl BudgetState { return Err(BudgetError::SourceIsPendingContract); } match instruction { - Instruction::NewBudget(budget) => { - let budget = budget.clone(); - if let Some(payment) = budget.final_payment() { + Instruction::NewBudget(expr) => { + let expr = expr.clone(); + if let Some(payment) = expr.final_payment() { accounts[1].tokens += payment.tokens; Ok(()) } else { @@ -132,7 +132,7 @@ impl BudgetState { Err(BudgetError::ContractAlreadyExists) } else { let mut state = BudgetState::default(); - state.pending_budget = Some(budget); + state.pending_budget = Some(expr); accounts[1].tokens += accounts[0].tokens; accounts[0].tokens = 0; state.initialized = true; diff --git a/src/budget_transaction.rs b/src/budget_transaction.rs index 06c8d017ef..c8eac3ca84 100644 --- a/src/budget_transaction.rs +++ b/src/budget_transaction.rs @@ -1,7 +1,7 @@ //! The `budget_transaction` module provides functionality for creating Budget transactions. use bincode::{deserialize, serialize}; -use budget::{Budget, Condition}; +use budget_expr::{BudgetExpr, Condition}; use budget_instruction::Instruction; use budget_program::BudgetState; use chrono::prelude::*; @@ -84,7 +84,7 @@ impl BudgetTransaction for Transaction { tokens: tokens - fee, to, }; - let budget_instruction = Instruction::NewBudget(Budget::Pay(payment)); + let budget_instruction = Instruction::NewBudget(BudgetExpr::Pay(payment)); let pay_userdata = serialize(&budget_instruction).unwrap(); let program_ids = vec![SystemProgram::id(), BudgetState::id()]; @@ -160,15 +160,15 @@ impl BudgetTransaction for Transaction { tokens: i64, last_id: Hash, ) -> Self { - let budget = if let Some(from) = cancelable { - Budget::Or( + let expr = if let Some(from) = cancelable { + BudgetExpr::Or( (Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ) } else { - Budget::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }) + BudgetExpr::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }) }; - let instruction = Instruction::NewBudget(budget); + let instruction = Instruction::NewBudget(expr); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new( from_keypair, @@ -189,15 +189,15 @@ impl BudgetTransaction for Transaction { tokens: i64, last_id: Hash, ) -> Self { - let budget = if let Some(from) = cancelable { - Budget::Or( + let expr = if let Some(from) = cancelable { + BudgetExpr::Or( (Condition::Signature(witness), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ) } else { - Budget::After(Condition::Signature(witness), Payment { tokens, to }) + BudgetExpr::After(Condition::Signature(witness), Payment { tokens, to }) }; - let instruction = Instruction::NewBudget(budget); + let instruction = Instruction::NewBudget(expr); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new( from_keypair, @@ -220,8 +220,8 @@ impl BudgetTransaction for Transaction { /// Verify only the payment plan. fn verify_plan(&self) -> bool { if let Some(SystemProgram::Move { tokens }) = self.system_instruction(0) { - if let Some(Instruction::NewBudget(budget)) = self.instruction(1) { - if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) { + if let Some(Instruction::NewBudget(expr)) = self.instruction(1) { + if !(self.fee >= 0 && self.fee <= tokens && expr.verify(tokens - self.fee)) { return false; } } @@ -267,11 +267,11 @@ mod tests { #[test] fn test_serialize_claim() { - let budget = Budget::Pay(Payment { + let expr = BudgetExpr::Pay(Payment { tokens: 0, to: Default::default(), }); - let instruction = Instruction::NewBudget(budget); + let instruction = Instruction::NewBudget(expr); let userdata = serialize(&instruction).unwrap(); let instructions = vec![transaction::Instruction { program_ids_index: 0, @@ -301,8 +301,8 @@ mod tests { if let SystemProgram::Move { ref mut tokens } = system_instruction { *tokens = 1_000_000; // <-- attack, part 1! let mut instruction = tx.instruction(1).unwrap(); - if let Instruction::NewBudget(ref mut budget) = instruction { - if let Budget::Pay(ref mut payment) = budget { + if let Instruction::NewBudget(ref mut expr) = instruction { + if let BudgetExpr::Pay(ref mut payment) = expr { payment.tokens = *tokens; // <-- attack, part 2! } } @@ -322,8 +322,8 @@ mod tests { let zero = Hash::default(); let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero); let mut instruction = tx.instruction(1); - if let Some(Instruction::NewBudget(ref mut budget)) = instruction { - if let Budget::Pay(ref mut payment) = budget { + if let Some(Instruction::NewBudget(ref mut expr)) = instruction { + if let BudgetExpr::Pay(ref mut payment) = expr { payment.to = thief_keypair.pubkey(); // <-- attack! } } @@ -339,8 +339,8 @@ mod tests { let zero = Hash::default(); let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero); let mut instruction = tx.instruction(1).unwrap(); - if let Instruction::NewBudget(ref mut budget) = instruction { - if let Budget::Pay(ref mut payment) = budget { + if let Instruction::NewBudget(ref mut expr) = instruction { + if let BudgetExpr::Pay(ref mut payment) = expr { payment.tokens = 2; // <-- attack! } } @@ -349,8 +349,8 @@ mod tests { // Also, ensure all branchs of the plan spend all tokens let mut instruction = tx.instruction(1).unwrap(); - if let Instruction::NewBudget(ref mut budget) = instruction { - if let Budget::Pay(ref mut payment) = budget { + if let Instruction::NewBudget(ref mut expr) = instruction { + if let BudgetExpr::Pay(ref mut payment) = expr { payment.tokens = 0; // <-- whoops! } } diff --git a/src/lib.rs b/src/lib.rs index 76ea1bba27..e2c9ff0f2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub mod banking_stage; pub mod blob_fetch_stage; pub mod bpf_loader; pub mod broadcast_stage; -pub mod budget; +pub mod budget_expr; pub mod budget_instruction; pub mod budget_transaction; #[cfg(feature = "chacha")] diff --git a/src/payment_plan.rs b/src/payment_plan.rs index 7f44d75e07..5450bed5cb 100644 --- a/src/payment_plan.rs +++ b/src/payment_plan.rs @@ -1,4 +1,4 @@ -//! The `plan` module provides a domain-specific language for payment plans. Users create Budget objects that +//! The `plan` module provides a domain-specific language for payment plans. Users create BudgetExpr objects that //! are given to an interpreter. The interpreter listens for `Witness` transactions, //! which it uses to reduce the payment plan. When the plan is reduced to a //! `Payment`, the payment is executed.