Remove 'Plan' indirection since it's implied by BUDGET_CONTRACT_ID

This commit is contained in:
Greg Fitzgerald 2018-09-20 13:50:11 -06:00
parent 0172422961
commit 3163fbad0e
3 changed files with 38 additions and 70 deletions

View File

@ -1,8 +1,9 @@
//! budget contract
use bank::Account;
use bincode::{self, deserialize, serialize_into, serialized_size};
use budget::Budget;
use chrono::prelude::{DateTime, Utc};
use instruction::{Instruction, Plan};
use instruction::Instruction;
use payment_plan::{PaymentPlan, Witness};
use signature::Pubkey;
use std::io;
@ -23,7 +24,7 @@ pub enum BudgetError {
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct BudgetContract {
pub initialized: bool,
pub pending_plan: Option<Plan>,
pub pending_budget: Option<Budget>,
pub last_error: Option<BudgetError>,
}
@ -32,7 +33,7 @@ pub const BUDGET_CONTRACT_ID: [u8; 32] = [
];
impl BudgetContract {
fn is_pending(&self) -> bool {
self.pending_plan != None
self.pending_budget != None
}
pub fn id() -> Pubkey {
Pubkey::new(&BUDGET_CONTRACT_ID)
@ -49,9 +50,9 @@ impl BudgetContract {
account: &mut [Account],
) -> Result<(), BudgetError> {
let mut final_payment = None;
if let Some(ref mut plan) = self.pending_plan {
plan.apply_witness(&Witness::Signature, &keys[0]);
final_payment = plan.final_payment();
if let Some(ref mut budget) = self.pending_budget {
budget.apply_witness(&Witness::Signature, &keys[0]);
final_payment = budget.final_payment();
}
if let Some(payment) = final_payment {
@ -59,7 +60,7 @@ impl BudgetContract {
trace!("destination missing");
return Err(BudgetError::DestinationMissing(payment.to));
}
self.pending_plan = None;
self.pending_budget = None;
account[1].tokens -= payment.tokens;
account[2].tokens += payment.tokens;
}
@ -77,9 +78,9 @@ impl BudgetContract {
// Check to see if any timelocked transactions can be completed.
let mut final_payment = None;
if let Some(ref mut plan) = self.pending_plan {
plan.apply_witness(&Witness::Timestamp(dt), &keys[0]);
final_payment = plan.final_payment();
if let Some(ref mut budget) = self.pending_budget {
budget.apply_witness(&Witness::Timestamp(dt), &keys[0]);
final_payment = budget.final_payment();
}
if let Some(payment) = final_payment {
@ -87,7 +88,7 @@ impl BudgetContract {
trace!("destination missing");
return Err(BudgetError::DestinationMissing(payment.to));
}
self.pending_plan = None;
self.pending_budget = None;
accounts[1].tokens -= payment.tokens;
accounts[2].tokens += payment.tokens;
}
@ -133,8 +134,8 @@ impl BudgetContract {
) -> Result<(), BudgetError> {
match instruction {
Instruction::NewContract(contract) => {
let plan = contract.plan.clone();
if let Some(payment) = plan.final_payment() {
let budget = contract.budget.clone();
if let Some(payment) = budget.final_payment() {
accounts[1].tokens += payment.tokens;
Ok(())
} else {
@ -144,7 +145,7 @@ impl BudgetContract {
Err(BudgetError::ContractAlreadyExists(tx.keys[1]))
} else {
let mut state = BudgetContract::default();
state.pending_plan = Some(plan);
state.pending_budget = Some(budget);
accounts[1].tokens += contract.tokens;
state.initialized = true;
state.serialize(&mut accounts[1].userdata);
@ -445,9 +446,9 @@ mod test {
assert_eq!(
tx.userdata,
vec![
0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7,
6, 5, 4, 1, 1, 1
0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 1,
1, 1
]
);
@ -456,16 +457,16 @@ mod test {
assert_eq!(
tx.userdata,
vec![
0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0,
0, 0, 0, 0, 0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58,
49, 49, 90, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206,
105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 9, 8, 7, 6, 5, 4, 1, 1, 1, 1, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135,
0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58, 49, 49, 90,
32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231,
150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
8, 7, 6, 5, 4, 1, 1, 1, 1, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167,
181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134,
137, 247, 161, 68, 192, 0, 0, 0, 0, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135,
187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180,
72, 134, 137, 247, 161, 68, 192, 0, 0, 0, 0, 0, 0, 0, 32, 253, 186, 201, 177, 11,
117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16,
252, 180, 72, 134, 137, 247, 161, 68
72, 134, 137, 247, 161, 68
]
);

View File

@ -1,42 +1,12 @@
use budget::Budget;
use chrono::prelude::{DateTime, Utc};
use payment_plan::{Payment, PaymentPlan, Witness};
use signature::Pubkey;
/// The type of payment plan. Each item must implement the PaymentPlan trait.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan {
/// The builtin contract language Budget.
Budget(Budget),
}
// A proxy for the underlying DSL.
impl PaymentPlan for Plan {
fn final_payment(&self) -> Option<Payment> {
match self {
Plan::Budget(budget) => budget.final_payment(),
}
}
fn verify(&self, spendable_tokens: i64) -> bool {
match self {
Plan::Budget(budget) => budget.verify(spendable_tokens),
}
}
fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) {
match self {
Plan::Budget(budget) => budget.apply_witness(witness, from),
}
}
}
/// A smart contract.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Contract {
/// The number of tokens allocated to the `Plan` and any transaction fees.
/// The number of tokens allocated to the `Budget` and any transaction fees.
pub tokens: i64,
pub plan: Plan,
pub budget: Budget,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote {

View File

@ -5,7 +5,7 @@ use budget::{Budget, Condition};
use budget_contract::BudgetContract;
use chrono::prelude::*;
use hash::Hash;
use instruction::{Contract, Instruction, Plan, Vote};
use instruction::{Contract, Instruction, Vote};
use payment_plan::{Payment, PaymentPlan};
use signature::{Keypair, KeypairUtil, Pubkey, Signature};
use std::mem::size_of;
@ -84,8 +84,7 @@ impl Transaction {
to: contract,
};
let budget = Budget::Pay(payment);
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens });
let instruction = Instruction::NewContract(Contract { budget, tokens });
let userdata = serialize(&instruction).unwrap();
Self::new_with_userdata(
from_keypair,
@ -168,8 +167,7 @@ impl Transaction {
(Condition::Timestamp(dt, from), Payment { tokens, to }),
(Condition::Signature(from), Payment { tokens, to: from }),
);
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens });
let instruction = Instruction::NewContract(Contract { budget, tokens });
let userdata = serialize(&instruction).expect("serialize instruction");
Self::new_with_userdata(
from_keypair,
@ -296,7 +294,7 @@ impl Transaction {
if let Some(Instruction::NewContract(contract)) = self.instruction() {
self.fee >= 0
&& self.fee <= contract.tokens
&& contract.plan.verify(contract.tokens - self.fee)
&& contract.budget.verify(contract.tokens - self.fee)
} else {
true
}
@ -363,8 +361,7 @@ mod tests {
tokens: 0,
to: Default::default(),
});
let plan = Plan::Budget(budget);
let instruction = Instruction::NewContract(Contract { plan, tokens: 0 });
let instruction = Instruction::NewContract(Contract { budget, tokens: 0 });
let userdata = serialize(&instruction).unwrap();
let claim0 = Transaction {
keys: vec![],
@ -388,7 +385,7 @@ mod tests {
let mut instruction = tx.instruction().unwrap();
if let Instruction::NewContract(ref mut contract) = instruction {
contract.tokens = 1_000_000; // <-- attack, part 1!
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
if let Budget::Pay(ref mut payment) = contract.budget {
payment.tokens = contract.tokens; // <-- attack, part 2!
}
}
@ -407,7 +404,7 @@ mod tests {
let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
let mut instruction = tx.instruction();
if let Some(Instruction::NewContract(ref mut contract)) = instruction {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
if let Budget::Pay(ref mut payment) = contract.budget {
payment.to = thief_keypair.pubkey(); // <-- attack!
}
}
@ -461,7 +458,7 @@ mod tests {
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
let mut instruction = tx.instruction().unwrap();
if let Instruction::NewContract(ref mut contract) = instruction {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
if let Budget::Pay(ref mut payment) = contract.budget {
payment.tokens = 2; // <-- attack!
}
}
@ -471,7 +468,7 @@ mod tests {
// Also, ensure all branchs of the plan spend all tokens
let mut instruction = tx.instruction().unwrap();
if let Instruction::NewContract(ref mut contract) = instruction {
if let Plan::Budget(Budget::Pay(ref mut payment)) = contract.plan {
if let Budget::Pay(ref mut payment) = contract.budget {
payment.tokens = 0; // <-- whoops!
}
}