Make conditions explicit in races

And boot recursive spending plans. That path required heap allocations.
Since we don't have a need for this generality right now, reduce the
language to the smallest one that can pass our test suite.
This commit is contained in:
Greg Fitzgerald
2018-03-17 14:42:58 -06:00
parent e5bae0604b
commit f4e0d1be58
3 changed files with 44 additions and 88 deletions

View File

@ -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}; use plan::{Action, Plan, PlanEvent};
use transaction::Transaction; use transaction::Transaction;
use signature::{KeyPair, PublicKey, Signature}; use signature::{KeyPair, PublicKey, Signature};
use mint::Mint; use mint::Mint;
@ -141,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);
@ -154,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
}; };
@ -186,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());
} }
} }

View File

@ -10,6 +10,23 @@ pub enum Condition {
Signature(PublicKey), 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)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Action { pub enum Action {
Pay(Payment), Pay(Payment),
@ -18,7 +35,7 @@ pub enum Action {
impl Action { impl Action {
pub fn spendable(&self) -> i64 { pub fn spendable(&self) -> i64 {
match *self { match *self {
Action::Pay(ref payment) => payment.asset.clone(), Action::Pay(ref payment) => payment.asset,
} }
} }
} }
@ -32,90 +49,41 @@ 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), Action(Action),
After(Condition, Box<Plan>), After(Condition, Action),
Race(Box<Plan>, Box<Plan>), Race((Condition, Action), (Condition, Action)),
} }
impl Plan { impl Plan {
pub fn verify(&self, spendable_assets: i64) -> bool { pub fn verify(&self, spendable_assets: i64) -> bool {
match *self { match *self {
Plan::Action(ref action) => action.spendable() == spendable_assets, Plan::Action(ref action) => action.spendable() == spendable_assets,
Plan::Race(ref plan_a, ref plan_b) => { Plan::After(_, ref action) => action.spendable() == spendable_assets,
plan_a.verify(spendable_assets) && plan_b.verify(spendable_assets) Plan::Race(ref a, ref b) => {
a.1.spendable() == spendable_assets && b.1.spendable() == spendable_assets
} }
Plan::After(_, ref plan) => plan.verify(spendable_assets),
} }
} }
pub fn run_race(&mut self) -> bool { pub fn process_event(&mut self, event: PlanEvent) -> bool {
let new_plan = if let Plan::Race(ref a, ref b) = *self { let mut new_action = None;
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 { match *self {
Plan::Action(_) => return true, Plan::Action(_) => return true,
Plan::Race(ref mut plan_a, ref mut plan_b) => { Plan::After(ref cond, ref action) => {
plan_a.process_verified_sig(from); if cond.is_satisfied(&event) {
plan_b.process_verified_sig(from); new_action = Some(action.clone());
}
Plan::After(Condition::Signature(pubkey), ref plan) => {
if from == pubkey {
new_plan = Some((**plan).clone());
} }
} }
_ => (), Plan::Race(ref a, ref b) => {
} if a.0.is_satisfied(&event) {
if self.run_race() { new_action = Some(a.1.clone());
return true; } else if b.0.is_satisfied(&event) {
} new_action = Some(b.1.clone());
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 plan) => {
if dt <= last_time {
new_plan = Some((**plan).clone());
} }
} }
_ => (),
}
if self.run_race() {
return true;
} }
if let Some(plan) = new_plan { if let Some(action) = new_action {
mem::replace(self, plan); mem::replace(self, Plan::Action(action));
true true
} else { } else {
false false

View File

@ -18,10 +18,7 @@ pub struct Transaction {
impl Transaction { impl Transaction {
pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: i64, 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,
@ -42,20 +39,11 @@ impl Transaction {
) -> 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), (
Box::new(Plan::Action(Action::Pay(Payment {
asset: asset.clone(),
to,
}))),
)),
Box::new(Plan::After(
Condition::Signature(from), Condition::Signature(from),
Box::new(Plan::Action(Action::Pay(Payment { Action::Pay(Payment { asset, to: from }),
asset: asset.clone(), ),
to: from,
}))),
)),
); );
let mut tr = Transaction { let mut tr = Transaction {
from, from,