Remove 'Plan' indirection since it's implied by BUDGET_CONTRACT_ID
This commit is contained in:
parent
0172422961
commit
3163fbad0e
|
@ -1,8 +1,9 @@
|
||||||
//! budget contract
|
//! budget contract
|
||||||
use bank::Account;
|
use bank::Account;
|
||||||
use bincode::{self, deserialize, serialize_into, serialized_size};
|
use bincode::{self, deserialize, serialize_into, serialized_size};
|
||||||
|
use budget::Budget;
|
||||||
use chrono::prelude::{DateTime, Utc};
|
use chrono::prelude::{DateTime, Utc};
|
||||||
use instruction::{Instruction, Plan};
|
use instruction::Instruction;
|
||||||
use payment_plan::{PaymentPlan, Witness};
|
use payment_plan::{PaymentPlan, Witness};
|
||||||
use signature::Pubkey;
|
use signature::Pubkey;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -23,7 +24,7 @@ pub enum BudgetError {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
|
||||||
pub struct BudgetContract {
|
pub struct BudgetContract {
|
||||||
pub initialized: bool,
|
pub initialized: bool,
|
||||||
pub pending_plan: Option<Plan>,
|
pub pending_budget: Option<Budget>,
|
||||||
pub last_error: Option<BudgetError>,
|
pub last_error: Option<BudgetError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ pub const BUDGET_CONTRACT_ID: [u8; 32] = [
|
||||||
];
|
];
|
||||||
impl BudgetContract {
|
impl BudgetContract {
|
||||||
fn is_pending(&self) -> bool {
|
fn is_pending(&self) -> bool {
|
||||||
self.pending_plan != None
|
self.pending_budget != None
|
||||||
}
|
}
|
||||||
pub fn id() -> Pubkey {
|
pub fn id() -> Pubkey {
|
||||||
Pubkey::new(&BUDGET_CONTRACT_ID)
|
Pubkey::new(&BUDGET_CONTRACT_ID)
|
||||||
|
@ -49,9 +50,9 @@ impl BudgetContract {
|
||||||
account: &mut [Account],
|
account: &mut [Account],
|
||||||
) -> Result<(), BudgetError> {
|
) -> Result<(), BudgetError> {
|
||||||
let mut final_payment = None;
|
let mut final_payment = None;
|
||||||
if let Some(ref mut plan) = self.pending_plan {
|
if let Some(ref mut budget) = self.pending_budget {
|
||||||
plan.apply_witness(&Witness::Signature, &keys[0]);
|
budget.apply_witness(&Witness::Signature, &keys[0]);
|
||||||
final_payment = plan.final_payment();
|
final_payment = budget.final_payment();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(payment) = final_payment {
|
if let Some(payment) = final_payment {
|
||||||
|
@ -59,7 +60,7 @@ impl BudgetContract {
|
||||||
trace!("destination missing");
|
trace!("destination missing");
|
||||||
return Err(BudgetError::DestinationMissing(payment.to));
|
return Err(BudgetError::DestinationMissing(payment.to));
|
||||||
}
|
}
|
||||||
self.pending_plan = None;
|
self.pending_budget = None;
|
||||||
account[1].tokens -= payment.tokens;
|
account[1].tokens -= payment.tokens;
|
||||||
account[2].tokens += payment.tokens;
|
account[2].tokens += payment.tokens;
|
||||||
}
|
}
|
||||||
|
@ -77,9 +78,9 @@ impl BudgetContract {
|
||||||
// Check to see if any timelocked transactions can be completed.
|
// Check to see if any timelocked transactions can be completed.
|
||||||
let mut final_payment = None;
|
let mut final_payment = None;
|
||||||
|
|
||||||
if let Some(ref mut plan) = self.pending_plan {
|
if let Some(ref mut budget) = self.pending_budget {
|
||||||
plan.apply_witness(&Witness::Timestamp(dt), &keys[0]);
|
budget.apply_witness(&Witness::Timestamp(dt), &keys[0]);
|
||||||
final_payment = plan.final_payment();
|
final_payment = budget.final_payment();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(payment) = final_payment {
|
if let Some(payment) = final_payment {
|
||||||
|
@ -87,7 +88,7 @@ impl BudgetContract {
|
||||||
trace!("destination missing");
|
trace!("destination missing");
|
||||||
return Err(BudgetError::DestinationMissing(payment.to));
|
return Err(BudgetError::DestinationMissing(payment.to));
|
||||||
}
|
}
|
||||||
self.pending_plan = None;
|
self.pending_budget = None;
|
||||||
accounts[1].tokens -= payment.tokens;
|
accounts[1].tokens -= payment.tokens;
|
||||||
accounts[2].tokens += payment.tokens;
|
accounts[2].tokens += payment.tokens;
|
||||||
}
|
}
|
||||||
|
@ -133,8 +134,8 @@ impl BudgetContract {
|
||||||
) -> Result<(), BudgetError> {
|
) -> Result<(), BudgetError> {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::NewContract(contract) => {
|
Instruction::NewContract(contract) => {
|
||||||
let plan = contract.plan.clone();
|
let budget = contract.budget.clone();
|
||||||
if let Some(payment) = plan.final_payment() {
|
if let Some(payment) = budget.final_payment() {
|
||||||
accounts[1].tokens += payment.tokens;
|
accounts[1].tokens += payment.tokens;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,7 +145,7 @@ impl BudgetContract {
|
||||||
Err(BudgetError::ContractAlreadyExists(tx.keys[1]))
|
Err(BudgetError::ContractAlreadyExists(tx.keys[1]))
|
||||||
} else {
|
} else {
|
||||||
let mut state = BudgetContract::default();
|
let mut state = BudgetContract::default();
|
||||||
state.pending_plan = Some(plan);
|
state.pending_budget = Some(budget);
|
||||||
accounts[1].tokens += contract.tokens;
|
accounts[1].tokens += contract.tokens;
|
||||||
state.initialized = true;
|
state.initialized = true;
|
||||||
state.serialize(&mut accounts[1].userdata);
|
state.serialize(&mut accounts[1].userdata);
|
||||||
|
@ -445,9 +446,9 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.userdata,
|
tx.userdata,
|
||||||
vec![
|
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, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||||
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,
|
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,
|
||||||
6, 5, 4, 1, 1, 1
|
1, 1
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -456,16 +457,16 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.userdata,
|
tx.userdata,
|
||||||
vec![
|
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, 192, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58,
|
0, 50, 48, 49, 54, 45, 48, 55, 45, 48, 56, 84, 48, 57, 58, 49, 48, 58, 49, 49, 90,
|
||||||
49, 49, 90, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206,
|
32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231,
|
||||||
105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192,
|
150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, 192, 0, 0, 0,
|
||||||
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, 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,
|
||||||
0, 0, 9, 8, 7, 6, 5, 4, 1, 1, 1, 1, 0, 0, 0, 32, 253, 186, 201, 177, 11, 117, 135,
|
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,
|
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,
|
72, 134, 137, 247, 161, 68
|
||||||
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
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,12 @@
|
||||||
use budget::Budget;
|
use budget::Budget;
|
||||||
use chrono::prelude::{DateTime, Utc};
|
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.
|
/// A smart contract.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Contract {
|
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 tokens: i64,
|
||||||
pub plan: Plan,
|
pub budget: Budget,
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use budget::{Budget, Condition};
|
||||||
use budget_contract::BudgetContract;
|
use budget_contract::BudgetContract;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use instruction::{Contract, Instruction, Plan, Vote};
|
use instruction::{Contract, Instruction, Vote};
|
||||||
use payment_plan::{Payment, PaymentPlan};
|
use payment_plan::{Payment, PaymentPlan};
|
||||||
use signature::{Keypair, KeypairUtil, Pubkey, Signature};
|
use signature::{Keypair, KeypairUtil, Pubkey, Signature};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -84,8 +84,7 @@ impl Transaction {
|
||||||
to: contract,
|
to: contract,
|
||||||
};
|
};
|
||||||
let budget = Budget::Pay(payment);
|
let budget = Budget::Pay(payment);
|
||||||
let plan = Plan::Budget(budget);
|
let instruction = Instruction::NewContract(Contract { budget, tokens });
|
||||||
let instruction = Instruction::NewContract(Contract { plan, tokens });
|
|
||||||
let userdata = serialize(&instruction).unwrap();
|
let userdata = serialize(&instruction).unwrap();
|
||||||
Self::new_with_userdata(
|
Self::new_with_userdata(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -168,8 +167,7 @@ impl Transaction {
|
||||||
(Condition::Timestamp(dt, from), Payment { tokens, to }),
|
(Condition::Timestamp(dt, from), Payment { tokens, to }),
|
||||||
(Condition::Signature(from), Payment { tokens, to: from }),
|
(Condition::Signature(from), Payment { tokens, to: from }),
|
||||||
);
|
);
|
||||||
let plan = Plan::Budget(budget);
|
let instruction = Instruction::NewContract(Contract { budget, tokens });
|
||||||
let instruction = Instruction::NewContract(Contract { plan, tokens });
|
|
||||||
let userdata = serialize(&instruction).expect("serialize instruction");
|
let userdata = serialize(&instruction).expect("serialize instruction");
|
||||||
Self::new_with_userdata(
|
Self::new_with_userdata(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -296,7 +294,7 @@ impl Transaction {
|
||||||
if let Some(Instruction::NewContract(contract)) = self.instruction() {
|
if let Some(Instruction::NewContract(contract)) = self.instruction() {
|
||||||
self.fee >= 0
|
self.fee >= 0
|
||||||
&& self.fee <= contract.tokens
|
&& self.fee <= contract.tokens
|
||||||
&& contract.plan.verify(contract.tokens - self.fee)
|
&& contract.budget.verify(contract.tokens - self.fee)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -363,8 +361,7 @@ mod tests {
|
||||||
tokens: 0,
|
tokens: 0,
|
||||||
to: Default::default(),
|
to: Default::default(),
|
||||||
});
|
});
|
||||||
let plan = Plan::Budget(budget);
|
let instruction = Instruction::NewContract(Contract { budget, tokens: 0 });
|
||||||
let instruction = Instruction::NewContract(Contract { plan, tokens: 0 });
|
|
||||||
let userdata = serialize(&instruction).unwrap();
|
let userdata = serialize(&instruction).unwrap();
|
||||||
let claim0 = Transaction {
|
let claim0 = Transaction {
|
||||||
keys: vec![],
|
keys: vec![],
|
||||||
|
@ -388,7 +385,7 @@ mod tests {
|
||||||
let mut instruction = tx.instruction().unwrap();
|
let mut instruction = tx.instruction().unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
if let Instruction::NewContract(ref mut contract) = instruction {
|
||||||
contract.tokens = 1_000_000; // <-- attack, part 1!
|
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!
|
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 tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
|
||||||
let mut instruction = tx.instruction();
|
let mut instruction = tx.instruction();
|
||||||
if let Some(Instruction::NewContract(ref mut contract)) = 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!
|
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 tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
|
||||||
let mut instruction = tx.instruction().unwrap();
|
let mut instruction = tx.instruction().unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
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!
|
payment.tokens = 2; // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,7 +468,7 @@ mod tests {
|
||||||
// Also, ensure all branchs of the plan spend all tokens
|
// Also, ensure all branchs of the plan spend all tokens
|
||||||
let mut instruction = tx.instruction().unwrap();
|
let mut instruction = tx.instruction().unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
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!
|
payment.tokens = 0; // <-- whoops!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue