Merge pull request #66 from garious/conditional-plan
Simplify contract language
This commit is contained in:
commit
4f09e5d04c
|
@ -5,7 +5,8 @@
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use entry::Entry;
|
use entry::Entry;
|
||||||
use event::Event;
|
use event::Event;
|
||||||
use transaction::{Action, Plan, Transaction};
|
use plan::{Action, Plan, PlanEvent};
|
||||||
|
use transaction::Transaction;
|
||||||
use signature::{KeyPair, PublicKey, Signature};
|
use signature::{KeyPair, PublicKey, Signature};
|
||||||
use mint::Mint;
|
use mint::Mint;
|
||||||
use historian::{reserve_signature, Historian};
|
use historian::{reserve_signature, Historian};
|
||||||
|
@ -30,7 +31,7 @@ pub struct Accountant {
|
||||||
pub balances: HashMap<PublicKey, i64>,
|
pub balances: HashMap<PublicKey, i64>,
|
||||||
pub first_id: Hash,
|
pub first_id: Hash,
|
||||||
pub last_id: Hash,
|
pub last_id: Hash,
|
||||||
pending: HashMap<Signature, Plan<i64>>,
|
pending: HashMap<Signature, Plan>,
|
||||||
time_sources: HashSet<PublicKey>,
|
time_sources: HashSet<PublicKey>,
|
||||||
last_time: DateTime<Utc>,
|
last_time: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
@ -83,7 +84,7 @@ impl Accountant {
|
||||||
self.last_id
|
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 {
|
if let Plan::Action(Action::Pay(ref payment)) = *plan {
|
||||||
allow_deposits && *from == payment.to
|
allow_deposits && *from == payment.to
|
||||||
} else {
|
} else {
|
||||||
|
@ -91,7 +92,7 @@ impl Accountant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_transaction(self: &mut Self, tr: Transaction<i64>) -> Result<()> {
|
pub fn process_transaction(self: &mut Self, tr: Transaction) -> Result<()> {
|
||||||
if !tr.verify() {
|
if !tr.verify() {
|
||||||
return Err(AccountingError::InvalidTransfer);
|
return Err(AccountingError::InvalidTransfer);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ impl Accountant {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit funds to the 'to' party.
|
/// 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 let Plan::Action(Action::Pay(ref payment)) = *plan {
|
||||||
if self.balances.contains_key(&payment.to) {
|
if self.balances.contains_key(&payment.to) {
|
||||||
if let Some(x) = self.balances.get_mut(&payment.to) {
|
if let Some(x) = self.balances.get_mut(&payment.to) {
|
||||||
|
@ -126,7 +127,7 @@ impl Accountant {
|
||||||
|
|
||||||
fn process_verified_transaction(
|
fn process_verified_transaction(
|
||||||
self: &mut Self,
|
self: &mut Self,
|
||||||
tr: &Transaction<i64>,
|
tr: &Transaction,
|
||||||
allow_deposits: bool,
|
allow_deposits: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !reserve_signature(&mut self.historian.signatures, &tr.sig) {
|
if !reserve_signature(&mut self.historian.signatures, &tr.sig) {
|
||||||
|
@ -140,7 +141,7 @@ impl Accountant {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut plan = tr.plan.clone();
|
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 {
|
if !actionable {
|
||||||
self.pending.insert(tr.sig, plan);
|
self.pending.insert(tr.sig, plan);
|
||||||
|
@ -153,7 +154,7 @@ impl Accountant {
|
||||||
|
|
||||||
fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> {
|
fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> {
|
||||||
let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) {
|
let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) {
|
||||||
plan.process_verified_sig(from)
|
plan.process_event(PlanEvent::Signature(from))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
@ -185,7 +186,7 @@ impl Accountant {
|
||||||
// Check to see if any timelocked transactions can be completed.
|
// Check to see if any timelocked transactions can be completed.
|
||||||
let mut completed = vec![];
|
let mut completed = vec![];
|
||||||
for (key, plan) in &mut self.pending {
|
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());
|
completed.push(key.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub struct AccountantSkel {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
Transaction(Transaction<i64>),
|
Transaction(Transaction),
|
||||||
GetBalance { key: PublicKey },
|
GetBalance { key: PublicKey },
|
||||||
GetEntries { last_id: Hash },
|
GetEntries { last_id: Hash },
|
||||||
GetId { is_last: bool },
|
GetId { is_last: bool },
|
||||||
|
|
|
@ -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 req = Request::Transaction(tr);
|
||||||
let data = serialize(&req).unwrap();
|
let data = serialize(&req).unwrap();
|
||||||
self.socket.send_to(&data, &self.addr)
|
self.socket.send_to(&data, &self.addr)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use bincode::serialize;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Transaction(Transaction<i64>),
|
Transaction(Transaction),
|
||||||
Signature {
|
Signature {
|
||||||
from: PublicKey,
|
from: PublicKey,
|
||||||
tx_sig: Signature,
|
tx_sig: Signature,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![cfg_attr(feature = "unstable", feature(test))]
|
#![cfg_attr(feature = "unstable", feature(test))]
|
||||||
pub mod signature;
|
pub mod signature;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
|
pub mod plan;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl Mint {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use log::verify_slice;
|
use log::verify_slice;
|
||||||
use transaction::{Action, Plan};
|
use plan::{Action, Plan};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_events() {
|
fn test_create_events() {
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,147 +1,24 @@
|
||||||
//! The `transaction` crate provides functionality for creating log transactions.
|
//! The `transaction` crate provides functionality for creating log transactions.
|
||||||
|
|
||||||
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
|
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
|
||||||
use serde::Serialize;
|
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use std::mem;
|
use plan::{Action, Condition, Payment, Plan};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Condition {
|
pub struct Transaction {
|
||||||
Timestamp(DateTime<Utc>),
|
|
||||||
Signature(PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub enum Action<T> {
|
|
||||||
Pay(Payment<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> Action<T> {
|
|
||||||
pub fn spendable(&self) -> T {
|
|
||||||
match *self {
|
|
||||||
Action::Pay(ref payment) => payment.asset.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Payment<T> {
|
|
||||||
pub asset: T,
|
|
||||||
pub to: PublicKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub enum Plan<T> {
|
|
||||||
Action(Action<T>),
|
|
||||||
After(Condition, Action<T>),
|
|
||||||
Race(Box<Plan<T>>, Box<Plan<T>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + Eq> Plan<T> {
|
|
||||||
pub fn verify(&self, spendable_assets: &T) -> bool {
|
|
||||||
match *self {
|
|
||||||
Plan::Action(ref action) => action.spendable() == *spendable_assets,
|
|
||||||
Plan::Race(ref plan_a, ref plan_b) => {
|
|
||||||
plan_a.verify(spendable_assets) && plan_b.verify(spendable_assets)
|
|
||||||
}
|
|
||||||
Plan::After(_, ref action) => action.spendable() == *spendable_assets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_race(&mut self) -> bool {
|
|
||||||
let new_plan = if let Plan::Race(ref a, ref b) = *self {
|
|
||||||
if let Plan::Action(_) = **a {
|
|
||||||
Some((**a).clone())
|
|
||||||
} else if let Plan::Action(_) = **b {
|
|
||||||
Some((**b).clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(plan) = new_plan {
|
|
||||||
mem::replace(self, plan);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_verified_sig(&mut self, from: PublicKey) -> bool {
|
|
||||||
let mut new_plan = None;
|
|
||||||
match *self {
|
|
||||||
Plan::Action(_) => return true,
|
|
||||||
Plan::Race(ref mut plan_a, ref mut plan_b) => {
|
|
||||||
plan_a.process_verified_sig(from);
|
|
||||||
plan_b.process_verified_sig(from);
|
|
||||||
}
|
|
||||||
Plan::After(Condition::Signature(pubkey), ref action) => {
|
|
||||||
if from == pubkey {
|
|
||||||
new_plan = Some(Plan::Action(action.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
if self.run_race() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(plan) = new_plan {
|
|
||||||
mem::replace(self, plan);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_verified_timestamp(&mut self, last_time: DateTime<Utc>) -> bool {
|
|
||||||
let mut new_plan = None;
|
|
||||||
match *self {
|
|
||||||
Plan::Action(_) => return true,
|
|
||||||
Plan::Race(ref mut plan_a, ref mut plan_b) => {
|
|
||||||
plan_a.process_verified_timestamp(last_time);
|
|
||||||
plan_b.process_verified_timestamp(last_time);
|
|
||||||
}
|
|
||||||
Plan::After(Condition::Timestamp(dt), ref action) => {
|
|
||||||
if dt <= last_time {
|
|
||||||
new_plan = Some(Plan::Action(action.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
if self.run_race() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(plan) = new_plan {
|
|
||||||
mem::replace(self, plan);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Transaction<T> {
|
|
||||||
pub from: PublicKey,
|
pub from: PublicKey,
|
||||||
pub plan: Plan<T>,
|
pub plan: Plan,
|
||||||
pub asset: T,
|
pub asset: i64,
|
||||||
pub last_id: Hash,
|
pub last_id: Hash,
|
||||||
pub sig: Signature,
|
pub sig: Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Serialize + Clone + Eq> Transaction<T> {
|
impl Transaction {
|
||||||
pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self {
|
pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: i64, last_id: Hash) -> Self {
|
||||||
let from = from_keypair.pubkey();
|
let from = from_keypair.pubkey();
|
||||||
let plan = Plan::Action(Action::Pay(Payment {
|
let plan = Plan::Action(Action::Pay(Payment { asset, to }));
|
||||||
asset: asset.clone(),
|
|
||||||
to,
|
|
||||||
}));
|
|
||||||
let mut tr = Transaction {
|
let mut tr = Transaction {
|
||||||
from,
|
from,
|
||||||
plan,
|
plan,
|
||||||
|
@ -157,25 +34,16 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
|
||||||
from_keypair: &KeyPair,
|
from_keypair: &KeyPair,
|
||||||
to: PublicKey,
|
to: PublicKey,
|
||||||
dt: DateTime<Utc>,
|
dt: DateTime<Utc>,
|
||||||
asset: T,
|
asset: i64,
|
||||||
last_id: Hash,
|
last_id: Hash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let from = from_keypair.pubkey();
|
let from = from_keypair.pubkey();
|
||||||
let plan = Plan::Race(
|
let plan = Plan::Race(
|
||||||
Box::new(Plan::After(
|
(Condition::Timestamp(dt), Action::Pay(Payment { asset, to })),
|
||||||
Condition::Timestamp(dt),
|
(
|
||||||
Action::Pay(Payment {
|
|
||||||
asset: asset.clone(),
|
|
||||||
to,
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
Box::new(Plan::After(
|
|
||||||
Condition::Signature(from),
|
Condition::Signature(from),
|
||||||
Action::Pay(Payment {
|
Action::Pay(Payment { asset, to: from }),
|
||||||
asset: asset.clone(),
|
),
|
||||||
to: from,
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
let mut tr = Transaction {
|
let mut tr = Transaction {
|
||||||
from,
|
from,
|
||||||
|
@ -198,7 +66,7 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self) -> bool {
|
pub fn verify(&self) -> bool {
|
||||||
self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(&self.asset)
|
self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(self.asset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,14 +74,12 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use hash::hash;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_claim() {
|
fn test_claim() {
|
||||||
let keypair = KeyPair::new();
|
let keypair = KeyPair::new();
|
||||||
let asset = hash(b"hello, world");
|
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let tr0 = Transaction::new(&keypair, keypair.pubkey(), asset, zero);
|
let tr0 = Transaction::new(&keypair, keypair.pubkey(), 42, zero);
|
||||||
assert!(tr0.verify());
|
assert!(tr0.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,8 +89,7 @@ mod tests {
|
||||||
let keypair0 = KeyPair::new();
|
let keypair0 = KeyPair::new();
|
||||||
let keypair1 = KeyPair::new();
|
let keypair1 = KeyPair::new();
|
||||||
let pubkey1 = keypair1.pubkey();
|
let pubkey1 = keypair1.pubkey();
|
||||||
let asset = hash(b"hello, world");
|
let tr0 = Transaction::new(&keypair0, pubkey1, 42, zero);
|
||||||
let tr0 = Transaction::new(&keypair0, pubkey1, asset, zero);
|
|
||||||
assert!(tr0.verify());
|
assert!(tr0.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,12 +102,12 @@ mod tests {
|
||||||
let claim0 = Transaction {
|
let claim0 = Transaction {
|
||||||
from: Default::default(),
|
from: Default::default(),
|
||||||
plan,
|
plan,
|
||||||
asset: 0u8,
|
asset: 0,
|
||||||
last_id: Default::default(),
|
last_id: Default::default(),
|
||||||
sig: Default::default(),
|
sig: Default::default(),
|
||||||
};
|
};
|
||||||
let buf = serialize(&claim0).unwrap();
|
let buf = serialize(&claim0).unwrap();
|
||||||
let claim1: Transaction<u8> = deserialize(&buf).unwrap();
|
let claim1: Transaction = deserialize(&buf).unwrap();
|
||||||
assert_eq!(claim1, claim0);
|
assert_eq!(claim1, claim0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,9 +116,9 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let keypair = KeyPair::new();
|
let keypair = KeyPair::new();
|
||||||
let pubkey = keypair.pubkey();
|
let pubkey = keypair.pubkey();
|
||||||
let mut tr = Transaction::new(&keypair, pubkey, hash(b"hello, world"), zero);
|
let mut tr = Transaction::new(&keypair, pubkey, 42, zero);
|
||||||
tr.sign(&keypair);
|
tr.sign(&keypair);
|
||||||
tr.asset = hash(b"goodbye cruel world"); // <-- attack!
|
tr.asset = 1_000_000; // <-- attack!
|
||||||
assert!(!tr.verify());
|
assert!(!tr.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,8 +129,7 @@ mod tests {
|
||||||
let thief_keypair = KeyPair::new();
|
let thief_keypair = KeyPair::new();
|
||||||
let pubkey1 = keypair1.pubkey();
|
let pubkey1 = keypair1.pubkey();
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let asset = hash(b"hello, world");
|
let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero);
|
||||||
let mut tr = Transaction::new(&keypair0, pubkey1, asset, zero);
|
|
||||||
tr.sign(&keypair0);
|
tr.sign(&keypair0);
|
||||||
if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
|
if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
|
||||||
payment.to = thief_keypair.pubkey(); // <-- attack!
|
payment.to = thief_keypair.pubkey(); // <-- attack!
|
||||||
|
|
Loading…
Reference in New Issue