@ -5,7 +5,7 @@
|
|||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use entry::Entry;
|
use entry::Entry;
|
||||||
use event::Event;
|
use event::Event;
|
||||||
use plan::{Action, Plan, PlanEvent};
|
use plan::{Plan, Witness};
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
use signature::{KeyPair, PublicKey, Signature};
|
use signature::{KeyPair, PublicKey, Signature};
|
||||||
use mint::Mint;
|
use mint::Mint;
|
||||||
@ -13,6 +13,7 @@ use historian::{reserve_signature, Historian};
|
|||||||
use recorder::Signal;
|
use recorder::Signal;
|
||||||
use std::sync::mpsc::SendError;
|
use std::sync::mpsc::SendError;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::collections::hash_map::Entry::Occupied;
|
||||||
use std::result;
|
use std::result;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
@ -26,6 +27,19 @@ pub enum AccountingError {
|
|||||||
|
|
||||||
pub type Result<T> = result::Result<T, AccountingError>;
|
pub type Result<T> = result::Result<T, AccountingError>;
|
||||||
|
|
||||||
|
/// Commit funds to the 'to' party.
|
||||||
|
fn complete_transaction(balances: &mut HashMap<PublicKey, i64>, plan: &Plan) {
|
||||||
|
if let Plan::Pay(ref payment) = *plan {
|
||||||
|
if balances.contains_key(&payment.to) {
|
||||||
|
if let Some(x) = balances.get_mut(&payment.to) {
|
||||||
|
*x += payment.tokens;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
balances.insert(payment.to, payment.tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Accountant {
|
pub struct Accountant {
|
||||||
pub historian: Historian,
|
pub historian: Historian,
|
||||||
pub balances: HashMap<PublicKey, i64>,
|
pub balances: HashMap<PublicKey, i64>,
|
||||||
@ -85,7 +99,7 @@ impl Accountant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool {
|
fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool {
|
||||||
if let Plan::Action(Action::Pay(ref payment)) = *plan {
|
if let Plan::Pay(ref payment) = *plan {
|
||||||
allow_deposits && *from == payment.to
|
allow_deposits && *from == payment.to
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -112,19 +126,6 @@ impl Accountant {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit funds to the 'to' party.
|
|
||||||
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) {
|
|
||||||
*x += payment.tokens;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.balances.insert(payment.to, payment.tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_verified_transaction(
|
fn process_verified_transaction(
|
||||||
self: &mut Self,
|
self: &mut Self,
|
||||||
tr: &Transaction,
|
tr: &Transaction,
|
||||||
@ -141,29 +142,25 @@ impl Accountant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut plan = tr.plan.clone();
|
let mut plan = tr.plan.clone();
|
||||||
let actionable = plan.process_event(PlanEvent::Timestamp(self.last_time));
|
plan.apply_witness(Witness::Timestamp(self.last_time));
|
||||||
|
|
||||||
if !actionable {
|
if plan.is_complete() {
|
||||||
|
complete_transaction(&mut self.balances, &plan);
|
||||||
|
} else {
|
||||||
self.pending.insert(tr.sig, plan);
|
self.pending.insert(tr.sig, plan);
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.complete_transaction(&plan);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if let Occupied(mut e) = self.pending.entry(tx_sig) {
|
||||||
plan.process_event(PlanEvent::Signature(from))
|
e.get_mut().apply_witness(Witness::Signature(from));
|
||||||
} else {
|
if e.get().is_complete() {
|
||||||
false
|
complete_transaction(&mut self.balances, e.get());
|
||||||
};
|
e.remove_entry();
|
||||||
|
|
||||||
if actionable {
|
|
||||||
if let Some(plan) = self.pending.remove(&tx_sig) {
|
|
||||||
self.complete_transaction(&plan);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -186,15 +183,15 @@ 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_event(PlanEvent::Timestamp(self.last_time)) {
|
plan.apply_witness(Witness::Timestamp(self.last_time));
|
||||||
|
if plan.is_complete() {
|
||||||
|
complete_transaction(&mut self.balances, &plan);
|
||||||
completed.push(key.clone());
|
completed.push(key.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in completed {
|
for key in completed {
|
||||||
if let Some(plan) = self.pending.remove(&key) {
|
self.pending.remove(&key);
|
||||||
self.complete_transaction(&plan);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -288,7 +285,7 @@ mod tests {
|
|||||||
let mut acc = Accountant::new(&alice, None);
|
let mut acc = Accountant::new(&alice, None);
|
||||||
let bob_pubkey = KeyPair::new().pubkey();
|
let bob_pubkey = KeyPair::new().pubkey();
|
||||||
let mut tr = Transaction::new(&alice.keypair(), bob_pubkey, 1, alice.seed());
|
let mut tr = Transaction::new(&alice.keypair(), bob_pubkey, 1, alice.seed());
|
||||||
if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
|
if let Plan::Pay(ref mut payment) = tr.plan {
|
||||||
payment.tokens = 2; // <-- attack!
|
payment.tokens = 2; // <-- attack!
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -297,7 +294,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 Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
|
if let Plan::Pay(ref mut payment) = tr.plan {
|
||||||
payment.tokens = 0; // <-- whoops!
|
payment.tokens = 0; // <-- whoops!
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -58,13 +58,13 @@ impl Mint {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ledger::verify_slice;
|
use ledger::verify_slice;
|
||||||
use plan::{Action, Plan};
|
use plan::Plan;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_events() {
|
fn test_create_events() {
|
||||||
let mut events = Mint::new(100).create_events().into_iter();
|
let mut events = Mint::new(100).create_events().into_iter();
|
||||||
if let Event::Transaction(tr) = events.next().unwrap() {
|
if let Event::Transaction(tr) = events.next().unwrap() {
|
||||||
if let Plan::Action(Action::Pay(payment)) = tr.plan {
|
if let Plan::Pay(payment) = tr.plan {
|
||||||
assert_eq!(tr.from, payment.to);
|
assert_eq!(tr.from, payment.to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
128
src/plan.rs
128
src/plan.rs
@ -1,45 +1,33 @@
|
|||||||
//! The `plan` crate provides functionality for creating spending plans.
|
//! A domain-specific language for payment plans. Users create Plan objects that
|
||||||
|
//! are given to an interpreter. The interpreter listens for `Witness` events,
|
||||||
|
//! which it uses to reduce the payment plan. When the plan is reduced to a
|
||||||
|
//! `Payment`, the payment is executed.
|
||||||
|
|
||||||
use signature::PublicKey;
|
use signature::PublicKey;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
pub enum Witness {
|
||||||
|
Timestamp(DateTime<Utc>),
|
||||||
|
Signature(PublicKey),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Condition {
|
pub enum Condition {
|
||||||
Timestamp(DateTime<Utc>),
|
Timestamp(DateTime<Utc>),
|
||||||
Signature(PublicKey),
|
Signature(PublicKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PlanEvent {
|
|
||||||
Timestamp(DateTime<Utc>),
|
|
||||||
Signature(PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Condition {
|
impl Condition {
|
||||||
pub fn is_satisfied(&self, event: &PlanEvent) -> bool {
|
pub fn is_satisfied(&self, witness: &Witness) -> bool {
|
||||||
match (self, event) {
|
match (self, witness) {
|
||||||
(&Condition::Signature(ref pubkey), &PlanEvent::Signature(ref from)) => pubkey == from,
|
(&Condition::Signature(ref pubkey), &Witness::Signature(ref from)) => pubkey == from,
|
||||||
(&Condition::Timestamp(ref dt), &PlanEvent::Timestamp(ref last_time)) => {
|
(&Condition::Timestamp(ref dt), &Witness::Timestamp(ref last_time)) => dt <= last_time,
|
||||||
dt <= last_time
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => 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.tokens,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Payment {
|
pub struct Payment {
|
||||||
pub tokens: i64,
|
pub tokens: i64,
|
||||||
@ -48,28 +36,22 @@ pub struct Payment {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Plan {
|
pub enum Plan {
|
||||||
Action(Action),
|
Pay(Payment),
|
||||||
After(Condition, Action),
|
After(Condition, Payment),
|
||||||
Race((Condition, Action), (Condition, Action)),
|
Race((Condition, Payment), (Condition, Payment)),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plan {
|
impl Plan {
|
||||||
pub fn new_payment(tokens: i64, to: PublicKey) -> Self {
|
pub fn new_payment(tokens: i64, to: PublicKey) -> Self {
|
||||||
Plan::Action(Action::Pay(Payment { tokens, to }))
|
Plan::Pay(Payment { tokens, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
Plan::After(Condition::Signature(from), Payment { tokens, to })
|
||||||
Condition::Signature(from),
|
|
||||||
Action::Pay(Payment { tokens, to }),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
Plan::After(Condition::Timestamp(dt), Payment { tokens, to })
|
||||||
Condition::Timestamp(dt),
|
|
||||||
Action::Pay(Payment { tokens, to }),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_cancelable_future_payment(
|
pub fn new_cancelable_future_payment(
|
||||||
@ -79,50 +61,40 @@ impl Plan {
|
|||||||
to: PublicKey,
|
to: PublicKey,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Plan::Race(
|
Plan::Race(
|
||||||
(
|
(Condition::Timestamp(dt), Payment { tokens, to }),
|
||||||
Condition::Timestamp(dt),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
Action::Pay(Payment { tokens, to }),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Condition::Signature(from),
|
|
||||||
Action::Pay(Payment { tokens, to: from }),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_complete(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Plan::Pay(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn verify(&self, spendable_tokens: i64) -> bool {
|
pub fn verify(&self, spendable_tokens: i64) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Plan::Action(ref action) => action.spendable() == spendable_tokens,
|
Plan::Pay(ref payment) => payment.tokens == spendable_tokens,
|
||||||
Plan::After(_, ref action) => action.spendable() == spendable_tokens,
|
Plan::After(_, ref payment) => payment.tokens == spendable_tokens,
|
||||||
Plan::Race(ref a, ref b) => {
|
Plan::Race(ref a, ref b) => {
|
||||||
a.1.spendable() == spendable_tokens && b.1.spendable() == spendable_tokens
|
a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_event(&mut self, event: PlanEvent) -> bool {
|
/// Apply a witness to the spending plan to see if the plan can be reduced.
|
||||||
let mut new_action = None;
|
/// If so, modify the plan in-place.
|
||||||
match *self {
|
pub fn apply_witness(&mut self, witness: Witness) {
|
||||||
Plan::Action(_) => return true,
|
let new_payment = match *self {
|
||||||
Plan::After(ref cond, ref action) => {
|
Plan::After(ref cond, ref payment) if cond.is_satisfied(&witness) => Some(payment),
|
||||||
if cond.is_satisfied(&event) {
|
Plan::Race((ref cond, ref payment), _) if cond.is_satisfied(&witness) => Some(payment),
|
||||||
new_action = Some(action.clone());
|
Plan::Race(_, (ref cond, ref payment)) if cond.is_satisfied(&witness) => Some(payment),
|
||||||
}
|
_ => None,
|
||||||
}
|
}.map(|x| x.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 {
|
if let Some(payment) = new_payment {
|
||||||
mem::replace(self, Plan::Action(action));
|
mem::replace(self, Plan::Pay(payment));
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,16 +106,16 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_signature_satisfied() {
|
fn test_signature_satisfied() {
|
||||||
let sig = PublicKey::default();
|
let sig = PublicKey::default();
|
||||||
assert!(Condition::Signature(sig).is_satisfied(&PlanEvent::Signature(sig)));
|
assert!(Condition::Signature(sig).is_satisfied(&Witness::Signature(sig)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_timestamp_satisfied() {
|
fn test_timestamp_satisfied() {
|
||||||
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
||||||
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
|
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(&Witness::Timestamp(dt1)));
|
||||||
assert!(Condition::Timestamp(dt1).is_satisfied(&PlanEvent::Timestamp(dt2)));
|
assert!(Condition::Timestamp(dt1).is_satisfied(&Witness::Timestamp(dt2)));
|
||||||
assert!(!Condition::Timestamp(dt2).is_satisfied(&PlanEvent::Timestamp(dt1)));
|
assert!(!Condition::Timestamp(dt2).is_satisfied(&Witness::Timestamp(dt1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -163,7 +135,7 @@ mod tests {
|
|||||||
let to = PublicKey::default();
|
let to = PublicKey::default();
|
||||||
|
|
||||||
let mut plan = Plan::new_authorized_payment(from, 42, to);
|
let mut plan = Plan::new_authorized_payment(from, 42, to);
|
||||||
assert!(plan.process_event(PlanEvent::Signature(from)));
|
plan.apply_witness(Witness::Signature(from));
|
||||||
assert_eq!(plan, Plan::new_payment(42, to));
|
assert_eq!(plan, Plan::new_payment(42, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +145,7 @@ mod tests {
|
|||||||
let to = PublicKey::default();
|
let to = PublicKey::default();
|
||||||
|
|
||||||
let mut plan = Plan::new_future_payment(dt, 42, to);
|
let mut plan = Plan::new_future_payment(dt, 42, to);
|
||||||
assert!(plan.process_event(PlanEvent::Timestamp(dt)));
|
plan.apply_witness(Witness::Timestamp(dt));
|
||||||
assert_eq!(plan, Plan::new_payment(42, to));
|
assert_eq!(plan, Plan::new_payment(42, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,11 +156,11 @@ mod tests {
|
|||||||
let to = PublicKey::default();
|
let to = PublicKey::default();
|
||||||
|
|
||||||
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
|
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
|
||||||
assert!(plan.process_event(PlanEvent::Timestamp(dt)));
|
plan.apply_witness(Witness::Timestamp(dt));
|
||||||
assert_eq!(plan, Plan::new_payment(42, to));
|
assert_eq!(plan, Plan::new_payment(42, to));
|
||||||
|
|
||||||
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
|
let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to);
|
||||||
assert!(plan.process_event(PlanEvent::Signature(from)));
|
plan.apply_witness(Witness::Signature(from));
|
||||||
assert_eq!(plan, Plan::new_payment(42, from));
|
assert_eq!(plan, Plan::new_payment(42, from));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
|
|||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use plan::{Action, Condition, Payment, Plan};
|
use plan::{Condition, Payment, Plan};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@ -18,7 +18,7 @@ pub struct Transaction {
|
|||||||
impl Transaction {
|
impl Transaction {
|
||||||
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 from = from_keypair.pubkey();
|
let from = from_keypair.pubkey();
|
||||||
let plan = Plan::Action(Action::Pay(Payment { tokens, to }));
|
let plan = Plan::Pay(Payment { tokens, to });
|
||||||
let mut tr = Transaction {
|
let mut tr = Transaction {
|
||||||
from,
|
from,
|
||||||
plan,
|
plan,
|
||||||
@ -39,14 +39,8 @@ impl Transaction {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let from = from_keypair.pubkey();
|
let from = from_keypair.pubkey();
|
||||||
let plan = Plan::Race(
|
let plan = Plan::Race(
|
||||||
(
|
(Condition::Timestamp(dt), Payment { tokens, to }),
|
||||||
Condition::Timestamp(dt),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
Action::Pay(Payment { tokens, to }),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Condition::Signature(from),
|
|
||||||
Action::Pay(Payment { tokens, to: from }),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
let mut tr = Transaction {
|
let mut tr = Transaction {
|
||||||
from,
|
from,
|
||||||
@ -98,10 +92,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_claim() {
|
fn test_serialize_claim() {
|
||||||
let plan = Plan::Action(Action::Pay(Payment {
|
let plan = Plan::Pay(Payment {
|
||||||
tokens: 0,
|
tokens: 0,
|
||||||
to: Default::default(),
|
to: Default::default(),
|
||||||
}));
|
});
|
||||||
let claim0 = Transaction {
|
let claim0 = Transaction {
|
||||||
from: Default::default(),
|
from: Default::default(),
|
||||||
plan,
|
plan,
|
||||||
@ -134,7 +128,7 @@ mod tests {
|
|||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero);
|
let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero);
|
||||||
tr.sign(&keypair0);
|
tr.sign(&keypair0);
|
||||||
if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
|
if let Plan::Pay(ref mut payment) = tr.plan {
|
||||||
payment.to = thief_keypair.pubkey(); // <-- attack!
|
payment.to = thief_keypair.pubkey(); // <-- attack!
|
||||||
};
|
};
|
||||||
assert!(!tr.verify());
|
assert!(!tr.verify());
|
||||||
|
Reference in New Issue
Block a user