Skip to content

Commit

Permalink
Merge pull request #66 from garious/conditional-plan
Browse files Browse the repository at this point in the history
Simplify contract language
  • Loading branch information
garious authored Mar 19, 2018
2 parents e054238 + 434f321 commit 4f09e5d
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 169 deletions.
19 changes: 10 additions & 9 deletions src/accountant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use hash::Hash;
use entry::Entry;
use event::Event;
use transaction::{Action, Plan, Transaction};
use plan::{Action, Plan, PlanEvent};
use transaction::Transaction;
use signature::{KeyPair, PublicKey, Signature};
use mint::Mint;
use historian::{reserve_signature, Historian};
Expand All @@ -30,7 +31,7 @@ pub struct Accountant {
pub balances: HashMap<PublicKey, i64>,
pub first_id: Hash,
pub last_id: Hash,
pending: HashMap<Signature, Plan<i64>>,
pending: HashMap<Signature, Plan>,
time_sources: HashSet<PublicKey>,
last_time: DateTime<Utc>,
}
Expand Down Expand Up @@ -83,15 +84,15 @@ impl Accountant {
self.last_id
}

fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan<i64>) -> bool {
fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool {
if let Plan::Action(Action::Pay(ref payment)) = *plan {
allow_deposits && *from == payment.to
} else {
false
}
}

pub fn process_transaction(self: &mut Self, tr: Transaction<i64>) -> Result<()> {
pub fn process_transaction(self: &mut Self, tr: Transaction) -> Result<()> {
if !tr.verify() {
return Err(AccountingError::InvalidTransfer);
}
Expand All @@ -112,7 +113,7 @@ impl Accountant {
}

/// Commit funds to the 'to' party.
fn complete_transaction(self: &mut Self, plan: &Plan<i64>) {
fn complete_transaction(self: &mut Self, plan: &Plan) {
if let Plan::Action(Action::Pay(ref payment)) = *plan {
if self.balances.contains_key(&payment.to) {
if let Some(x) = self.balances.get_mut(&payment.to) {
Expand All @@ -126,7 +127,7 @@ impl Accountant {

fn process_verified_transaction(
self: &mut Self,
tr: &Transaction<i64>,
tr: &Transaction,
allow_deposits: bool,
) -> Result<()> {
if !reserve_signature(&mut self.historian.signatures, &tr.sig) {
Expand All @@ -140,7 +141,7 @@ impl Accountant {
}

let mut plan = tr.plan.clone();
let actionable = plan.process_verified_timestamp(self.last_time);
let actionable = plan.process_event(PlanEvent::Timestamp(self.last_time));

if !actionable {
self.pending.insert(tr.sig, plan);
Expand All @@ -153,7 +154,7 @@ impl Accountant {

fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> {
let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) {
plan.process_verified_sig(from)
plan.process_event(PlanEvent::Signature(from))
} else {
false
};
Expand Down Expand Up @@ -185,7 +186,7 @@ impl Accountant {
// Check to see if any timelocked transactions can be completed.
let mut completed = vec![];
for (key, plan) in &mut self.pending {
if plan.process_verified_timestamp(self.last_time) {
if plan.process_event(PlanEvent::Timestamp(self.last_time)) {
completed.push(key.clone());
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/accountant_skel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct AccountantSkel {

#[derive(Serialize, Deserialize, Debug)]
pub enum Request {
Transaction(Transaction<i64>),
Transaction(Transaction),
GetBalance { key: PublicKey },
GetEntries { last_id: Hash },
GetId { is_last: bool },
Expand Down
2 changes: 1 addition & 1 deletion src/accountant_stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl AccountantStub {
}
}

pub fn transfer_signed(&self, tr: Transaction<i64>) -> io::Result<usize> {
pub fn transfer_signed(&self, tr: Transaction) -> io::Result<usize> {
let req = Request::Transaction(tr);
let data = serialize(&req).unwrap();
self.socket.send_to(&data, &self.addr)
Expand Down
2 changes: 1 addition & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bincode::serialize;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Event {
Transaction(Transaction<i64>),
Transaction(Transaction),
Signature {
from: PublicKey,
tx_sig: Signature,
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg_attr(feature = "unstable", feature(test))]
pub mod signature;
pub mod hash;
pub mod plan;
pub mod transaction;
pub mod event;
pub mod entry;
Expand Down
2 changes: 1 addition & 1 deletion src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Mint {
mod tests {
use super::*;
use log::verify_slice;
use transaction::{Action, Plan};
use plan::{Action, Plan};

#[test]
fn test_create_events() {
Expand Down
188 changes: 188 additions & 0 deletions src/plan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//! The `plan` crate provides functionality for creating spending plans.

use signature::PublicKey;
use chrono::prelude::*;
use std::mem;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Condition {
Timestamp(DateTime<Utc>),
Signature(PublicKey),
}

pub enum PlanEvent {
Timestamp(DateTime<Utc>),
Signature(PublicKey),
}

impl Condition {
pub fn is_satisfied(&self, event: &PlanEvent) -> bool {
match (self, event) {
(&Condition::Signature(ref pubkey), &PlanEvent::Signature(ref from)) => pubkey == from,
(&Condition::Timestamp(ref dt), &PlanEvent::Timestamp(ref last_time)) => {
dt <= last_time
}
_ => false,
}
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Action {
Pay(Payment),
}

impl Action {
pub fn spendable(&self) -> i64 {
match *self {
Action::Pay(ref payment) => payment.asset,
}
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Payment {
pub asset: i64,
pub to: PublicKey,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan {
Action(Action),
After(Condition, Action),
Race((Condition, Action), (Condition, Action)),
}

impl Plan {
pub fn new_payment(asset: i64, to: PublicKey) -> Self {
Plan::Action(Action::Pay(Payment { asset, to }))
}

pub fn new_authorized_payment(from: PublicKey, asset: i64, to: PublicKey) -> Self {
Plan::After(
Condition::Signature(from),
Action::Pay(Payment { asset, to }),
)
}

pub fn new_future_payment(dt: DateTime<Utc>, asset: i64, to: PublicKey) -> Self {
Plan::After(Condition::Timestamp(dt), Action::Pay(Payment { asset, to }))
}

pub fn new_cancelable_future_payment(
dt: DateTime<Utc>,
from: PublicKey,
asset: i64,
to: PublicKey,
) -> Self {
Plan::Race(
(Condition::Timestamp(dt), Action::Pay(Payment { asset, to })),
(
Condition::Signature(from),
Action::Pay(Payment { asset, to: from }),
),
)
}

pub fn verify(&self, spendable_assets: i64) -> bool {
match *self {
Plan::Action(ref action) => action.spendable() == spendable_assets,
Plan::After(_, ref action) => action.spendable() == spendable_assets,
Plan::Race(ref a, ref b) => {
a.1.spendable() == spendable_assets && b.1.spendable() == spendable_assets
}
}
}

pub fn process_event(&mut self, event: PlanEvent) -> bool {
let mut new_action = None;
match *self {
Plan::Action(_) => return true,
Plan::After(ref cond, ref action) => {
if cond.is_satisfied(&event) {
new_action = Some(action.clone());
}
}
Plan::Race(ref a, ref b) => {
if a.0.is_satisfied(&event) {
new_action = Some(a.1.clone());
} else if b.0.is_satisfied(&event) {
new_action = Some(b.1.clone());
}
}
}

if let Some(action) = new_action {
mem::replace(self, Plan::Action(action));
true
} else {
false
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_signature_satisfied() {
let sig = PublicKey::default();
assert!(Condition::Signature(sig).is_satisfied(&PlanEvent::Signature(sig)));
}

#[test]
fn test_timestamp_satisfied() {
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
assert!(Condition::Timestamp(dt1).is_satisfied(&PlanEvent::Timestamp(dt1)));
assert!(Condition::Timestamp(dt1).is_satisfied(&PlanEvent::Timestamp(dt2)));
assert!(!Condition::Timestamp(dt2).is_satisfied(&PlanEvent::Timestamp(dt1)));
}

#[test]
fn test_verify_plan() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = PublicKey::default();
let to = PublicKey::default();
assert!(Plan::new_payment(42, to).verify(42));
assert!(Plan::new_authorized_payment(from, 42, to).verify(42));
assert!(Plan::new_future_payment(dt, 42, to).verify(42));
assert!(Plan::new_cancelable_future_payment(dt, from, 42, to).verify(42));
}

#[test]
fn test_authorized_payment() {
let from = PublicKey::default();
let to = PublicKey::default();

let mut plan = Plan::new_authorized_payment(from, 42, to);
assert!(plan.process_event(PlanEvent::Signature(from)));
assert_eq!(plan, Plan::new_payment(42, to));
}

#[test]
fn test_future_payment() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let to = PublicKey::default();

let mut plan = Plan::new_future_payment(dt, 42, to);
assert!(plan.process_event(PlanEvent::Timestamp(dt)));
assert_eq!(plan, Plan::new_payment(42, to));
}

#[test]
fn test_cancelable_future_payment() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = PublicKey::default();
let to = PublicKey::default();

let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
assert!(plan.process_event(PlanEvent::Timestamp(dt)));
assert_eq!(plan, Plan::new_payment(42, to));

let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
assert!(plan.process_event(PlanEvent::Signature(from)));
assert_eq!(plan, Plan::new_payment(42, from));
}
}
Loading

0 comments on commit 4f09e5d

Please sign in to comment.