Plan -> Budget

Budget is now an EDSL. PaymentPlan is the interface to it.
This commit is contained in:
Greg Fitzgerald
2018-05-29 13:08:59 -06:00
parent ad00d7bd9c
commit aa7e3df8d6
5 changed files with 42 additions and 42 deletions

View File

@ -9,7 +9,7 @@ use chrono::prelude::*;
use entry::Entry; use entry::Entry;
use hash::Hash; use hash::Hash;
use mint::Mint; use mint::Mint;
use plan::{Payment, PaymentPlan, Plan, Witness}; use plan::{Budget, Payment, PaymentPlan, Witness};
use rayon::prelude::*; use rayon::prelude::*;
use signature::{KeyPair, PublicKey, Signature}; use signature::{KeyPair, PublicKey, Signature};
use std::collections::hash_map::Entry::Occupied; use std::collections::hash_map::Entry::Occupied;
@ -32,7 +32,7 @@ pub type Result<T> = result::Result<T, BankError>;
pub struct Bank { pub struct Bank {
balances: RwLock<HashMap<PublicKey, AtomicIsize>>, balances: RwLock<HashMap<PublicKey, AtomicIsize>>,
pending: RwLock<HashMap<Signature, Plan>>, pending: RwLock<HashMap<Signature, Budget>>,
last_ids: RwLock<VecDeque<(Hash, RwLock<HashSet<Signature>>)>>, last_ids: RwLock<VecDeque<(Hash, RwLock<HashSet<Signature>>)>>,
time_sources: RwLock<HashSet<PublicKey>>, time_sources: RwLock<HashSet<PublicKey>>,
last_time: RwLock<DateTime<Utc>>, last_time: RwLock<DateTime<Utc>>,

View File

@ -69,7 +69,7 @@ pub struct MintDemo {
mod tests { mod tests {
use super::*; use super::*;
use ledger::Block; use ledger::Block;
use plan::Plan; use plan::Budget;
use transaction::Instruction; use transaction::Instruction;
#[test] #[test]
@ -77,7 +77,7 @@ mod tests {
let mut transactions = Mint::new(100).create_transactions().into_iter(); let mut transactions = Mint::new(100).create_transactions().into_iter();
let tx = transactions.next().unwrap(); let tx = transactions.next().unwrap();
if let Instruction::NewContract(contract) = tx.instruction { if let Instruction::NewContract(contract) = tx.instruction {
if let Plan::Pay(payment) = contract.plan { if let Budget::Pay(payment) = contract.plan {
assert_eq!(tx.from, payment.to); assert_eq!(tx.from, payment.to);
} }
} }

View File

@ -1,4 +1,4 @@
//! The `plan` module provides a domain-specific language for payment plans. Users create Plan objects that //! The `plan` module provides a domain-specific language for payment plans. Users create Budget objects that
//! are given to an interpreter. The interpreter listens for `Witness` transactions, //! 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 //! which it uses to reduce the payment plan. When the plan is reduced to a
//! `Payment`, the payment is executed. //! `Payment`, the payment is executed.
@ -50,26 +50,26 @@ pub trait PaymentPlan {
#[repr(C)] #[repr(C)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan { pub enum Budget {
Pay(Payment), Pay(Payment),
After(Condition, Payment), After(Condition, Payment),
Race((Condition, Payment), (Condition, Payment)), Race((Condition, Payment), (Condition, Payment)),
} }
impl Plan { impl Budget {
/// Create the simplest spending plan - one that pays `tokens` to PublicKey. /// Create the simplest spending plan - one that pays `tokens` to PublicKey.
pub fn new_payment(tokens: i64, to: PublicKey) -> Self { pub fn new_payment(tokens: i64, to: PublicKey) -> Self {
Plan::Pay(Payment { tokens, to }) Budget::Pay(Payment { tokens, to })
} }
/// Create a spending plan that pays `tokens` to `to` after being witnessed by `from`. /// Create a spending plan that pays `tokens` to `to` after being witnessed by `from`.
pub fn new_authorized_payment(from: PublicKey, tokens: i64, to: PublicKey) -> Self { pub fn new_authorized_payment(from: PublicKey, tokens: i64, to: PublicKey) -> Self {
Plan::After(Condition::Signature(from), Payment { tokens, to }) Budget::After(Condition::Signature(from), Payment { tokens, to })
} }
/// Create a spending plan that pays `tokens` to `to` after the given DateTime. /// Create a spending plan that pays `tokens` to `to` after the given DateTime.
pub fn new_future_payment(dt: DateTime<Utc>, tokens: i64, to: PublicKey) -> Self { pub fn new_future_payment(dt: DateTime<Utc>, tokens: i64, to: PublicKey) -> Self {
Plan::After(Condition::Timestamp(dt), Payment { tokens, to }) Budget::After(Condition::Timestamp(dt), Payment { tokens, to })
} }
/// Create a spending plan that pays `tokens` to `to` after the given DateTime /// Create a spending plan that pays `tokens` to `to` after the given DateTime
@ -80,18 +80,18 @@ impl Plan {
tokens: i64, tokens: i64,
to: PublicKey, to: PublicKey,
) -> Self { ) -> Self {
Plan::Race( Budget::Race(
(Condition::Timestamp(dt), Payment { tokens, to }), (Condition::Timestamp(dt), Payment { tokens, to }),
(Condition::Signature(from), Payment { tokens, to: from }), (Condition::Signature(from), Payment { tokens, to: from }),
) )
} }
} }
impl PaymentPlan for Plan { impl PaymentPlan for Budget {
/// Return Payment if the spending plan requires no additional Witnesses. /// Return Payment if the spending plan requires no additional Witnesses.
fn final_payment(&self) -> Option<Payment> { fn final_payment(&self) -> Option<Payment> {
match *self { match *self {
Plan::Pay(ref payment) => Some(payment.clone()), Budget::Pay(ref payment) => Some(payment.clone()),
_ => None, _ => None,
} }
} }
@ -99,10 +99,10 @@ impl PaymentPlan for Plan {
/// Return true if the plan spends exactly `spendable_tokens`. /// Return true if the plan spends exactly `spendable_tokens`.
fn verify(&self, spendable_tokens: i64) -> bool { fn verify(&self, spendable_tokens: i64) -> bool {
match *self { match *self {
Plan::Pay(ref payment) | Plan::After(_, ref payment) => { Budget::Pay(ref payment) | Budget::After(_, ref payment) => {
payment.tokens == spendable_tokens payment.tokens == spendable_tokens
} }
Plan::Race(ref a, ref b) => { Budget::Race(ref a, ref b) => {
a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens
} }
} }
@ -112,14 +112,14 @@ impl PaymentPlan for Plan {
/// If so, modify the plan in-place. /// If so, modify the plan in-place.
fn apply_witness(&mut self, witness: &Witness) { fn apply_witness(&mut self, witness: &Witness) {
let new_payment = match *self { let new_payment = match *self {
Plan::After(ref cond, ref payment) if cond.is_satisfied(witness) => Some(payment), Budget::After(ref cond, ref payment) if cond.is_satisfied(witness) => Some(payment),
Plan::Race((ref cond, ref payment), _) if cond.is_satisfied(witness) => Some(payment), Budget::Race((ref cond, ref payment), _) if cond.is_satisfied(witness) => Some(payment),
Plan::Race(_, (ref cond, ref payment)) if cond.is_satisfied(witness) => Some(payment), Budget::Race(_, (ref cond, ref payment)) if cond.is_satisfied(witness) => Some(payment),
_ => None, _ => None,
}.cloned(); }.cloned();
if let Some(payment) = new_payment { if let Some(payment) = new_payment {
mem::replace(self, Plan::Pay(payment)); mem::replace(self, Budget::Pay(payment));
} }
} }
} }
@ -148,10 +148,10 @@ mod tests {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = PublicKey::default(); let from = PublicKey::default();
let to = PublicKey::default(); let to = PublicKey::default();
assert!(Plan::new_payment(42, to).verify(42)); assert!(Budget::new_payment(42, to).verify(42));
assert!(Plan::new_authorized_payment(from, 42, to).verify(42)); assert!(Budget::new_authorized_payment(from, 42, to).verify(42));
assert!(Plan::new_future_payment(dt, 42, to).verify(42)); assert!(Budget::new_future_payment(dt, 42, to).verify(42));
assert!(Plan::new_cancelable_future_payment(dt, from, 42, to).verify(42)); assert!(Budget::new_cancelable_future_payment(dt, from, 42, to).verify(42));
} }
#[test] #[test]
@ -159,9 +159,9 @@ mod tests {
let from = PublicKey::default(); let from = PublicKey::default();
let to = PublicKey::default(); let to = PublicKey::default();
let mut plan = Plan::new_authorized_payment(from, 42, to); let mut plan = Budget::new_authorized_payment(from, 42, to);
plan.apply_witness(&Witness::Signature(from)); plan.apply_witness(&Witness::Signature(from));
assert_eq!(plan, Plan::new_payment(42, to)); assert_eq!(plan, Budget::new_payment(42, to));
} }
#[test] #[test]
@ -169,9 +169,9 @@ mod tests {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let to = PublicKey::default(); let to = PublicKey::default();
let mut plan = Plan::new_future_payment(dt, 42, to); let mut plan = Budget::new_future_payment(dt, 42, to);
plan.apply_witness(&Witness::Timestamp(dt)); plan.apply_witness(&Witness::Timestamp(dt));
assert_eq!(plan, Plan::new_payment(42, to)); assert_eq!(plan, Budget::new_payment(42, to));
} }
#[test] #[test]
@ -180,12 +180,12 @@ mod tests {
let from = PublicKey::default(); let from = PublicKey::default();
let to = PublicKey::default(); let to = PublicKey::default();
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); let mut plan = Budget::new_cancelable_future_payment(dt, from, 42, to);
plan.apply_witness(&Witness::Timestamp(dt)); plan.apply_witness(&Witness::Timestamp(dt));
assert_eq!(plan, Plan::new_payment(42, to)); assert_eq!(plan, Budget::new_payment(42, to));
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); let mut plan = Budget::new_cancelable_future_payment(dt, from, 42, to);
plan.apply_witness(&Witness::Signature(from)); plan.apply_witness(&Witness::Signature(from));
assert_eq!(plan, Plan::new_payment(42, from)); assert_eq!(plan, Budget::new_payment(42, from));
} }
} }

View File

@ -179,7 +179,7 @@ mod tests {
use futures::Future; use futures::Future;
use logger; use logger;
use mint::Mint; use mint::Mint;
use plan::Plan; use plan::Budget;
use server::Server; use server::Server;
use signature::{KeyPair, KeyPairUtil}; use signature::{KeyPair, KeyPairUtil};
use std::io::sink; use std::io::sink;
@ -282,7 +282,7 @@ mod tests {
let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id); let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id);
if let Instruction::NewContract(contract) = &mut tr2.instruction { if let Instruction::NewContract(contract) = &mut tr2.instruction {
contract.tokens = 502; contract.tokens = 502;
contract.plan = Plan::new_payment(502, bob_pubkey); contract.plan = Budget::new_payment(502, bob_pubkey);
} }
let _sig = client.transfer_signed(tr2).unwrap(); let _sig = client.transfer_signed(tr2).unwrap();

View File

@ -3,7 +3,7 @@
use bincode::serialize; use bincode::serialize;
use chrono::prelude::*; use chrono::prelude::*;
use hash::Hash; use hash::Hash;
use plan::{Condition, Payment, PaymentPlan, Plan}; use plan::{Budget, Condition, Payment, PaymentPlan};
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
pub const SIGNED_DATA_OFFSET: usize = 112; pub const SIGNED_DATA_OFFSET: usize = 112;
@ -13,7 +13,7 @@ pub const PUB_KEY_OFFSET: usize = 80;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Contract { pub struct Contract {
pub tokens: i64, pub tokens: i64,
pub plan: Plan, pub plan: Budget,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
@ -50,7 +50,7 @@ impl Transaction {
/// Create and sign a new Transaction. Used for unit-testing. /// Create and sign a new Transaction. Used for unit-testing.
pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self {
let plan = Plan::Pay(Payment { tokens, to }); let plan = Budget::Pay(Payment { tokens, to });
let instruction = Instruction::NewContract(Contract { plan, tokens }); let instruction = Instruction::NewContract(Contract { plan, tokens });
Self::new_from_instruction(from_keypair, instruction, last_id) Self::new_from_instruction(from_keypair, instruction, last_id)
} }
@ -76,7 +76,7 @@ impl Transaction {
last_id: Hash, last_id: Hash,
) -> Self { ) -> Self {
let from = from_keypair.pubkey(); let from = from_keypair.pubkey();
let plan = Plan::Race( let plan = Budget::Race(
(Condition::Timestamp(dt), Payment { tokens, to }), (Condition::Timestamp(dt), Payment { tokens, to }),
(Condition::Signature(from), Payment { tokens, to: from }), (Condition::Signature(from), Payment { tokens, to: from }),
); );
@ -162,7 +162,7 @@ mod tests {
#[test] #[test]
fn test_serialize_claim() { fn test_serialize_claim() {
let plan = Plan::Pay(Payment { let plan = Budget::Pay(Payment {
tokens: 0, tokens: 0,
to: Default::default(), to: Default::default(),
}); });
@ -186,7 +186,7 @@ mod tests {
let mut tx = Transaction::new(&keypair, pubkey, 42, zero); let mut tx = Transaction::new(&keypair, pubkey, 42, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction { if let Instruction::NewContract(contract) = &mut tx.instruction {
contract.tokens = 1_000_000; // <-- attack, part 1! contract.tokens = 1_000_000; // <-- attack, part 1!
if let Plan::Pay(ref mut payment) = contract.plan { if let Budget::Pay(ref mut payment) = contract.plan {
payment.tokens = contract.tokens; // <-- attack, part 2! payment.tokens = contract.tokens; // <-- attack, part 2!
} }
} }
@ -203,7 +203,7 @@ mod tests {
let zero = Hash::default(); let zero = Hash::default();
let mut tx = Transaction::new(&keypair0, pubkey1, 42, zero); let mut tx = Transaction::new(&keypair0, pubkey1, 42, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction { if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Plan::Pay(ref mut payment) = contract.plan { if let Budget::Pay(ref mut payment) = contract.plan {
payment.to = thief_keypair.pubkey(); // <-- attack! payment.to = thief_keypair.pubkey(); // <-- attack!
} }
} }
@ -227,7 +227,7 @@ mod tests {
let zero = Hash::default(); let zero = Hash::default();
let mut tx = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero); let mut tx = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero);
if let Instruction::NewContract(contract) = &mut tx.instruction { if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Plan::Pay(ref mut payment) = contract.plan { if let Budget::Pay(ref mut payment) = contract.plan {
payment.tokens = 2; // <-- attack! payment.tokens = 2; // <-- attack!
} }
} }
@ -235,7 +235,7 @@ mod tests {
// Also, ensure all branchs of the plan spend all tokens // Also, ensure all branchs of the plan spend all tokens
if let Instruction::NewContract(contract) = &mut tx.instruction { if let Instruction::NewContract(contract) = &mut tx.instruction {
if let Plan::Pay(ref mut payment) = contract.plan { if let Budget::Pay(ref mut payment) = contract.plan {
payment.tokens = 0; // <-- whoops! payment.tokens = 0; // <-- whoops!
} }
} }