Rename Budget to BudgetExpr

This commit is contained in:
Greg Fitzgerald 2018-11-02 20:13:33 -06:00 committed by Grimes
parent a264f8fa9b
commit c8c255ad73
6 changed files with 87 additions and 87 deletions

View File

@ -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<Utc>, 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<Payment> {
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));
}
}

View File

@ -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<Utc>),

View File

@ -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<Budget>,
pub pending_budget: Option<BudgetExpr>,
}
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;

View File

@ -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!
}
}

View File

@ -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")]

View File

@ -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.