Boot Contract type from Budget
In the old bank (before the contract engine), Contract wasn't specific to Budget. It provided the same service as what is now called SystemProgram::Move, but without requiring a separate account.
This commit is contained in:
parent
d0f43e9934
commit
15a89d4f17
|
@ -1,13 +1,6 @@
|
||||||
use budget::Budget;
|
use budget::Budget;
|
||||||
use chrono::prelude::{DateTime, Utc};
|
use chrono::prelude::{DateTime, Utc};
|
||||||
|
|
||||||
/// A smart contract.
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Contract {
|
|
||||||
/// The number of tokens allocated to the `Budget` and any transaction fees.
|
|
||||||
pub tokens: i64,
|
|
||||||
pub budget: Budget,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
/// We send some gossip specific membership information through the vote to shortcut
|
/// We send some gossip specific membership information through the vote to shortcut
|
||||||
|
@ -22,13 +15,13 @@ pub struct Vote {
|
||||||
/// An instruction to progress the smart contract.
|
/// An instruction to progress the smart contract.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
/// Declare and instantiate `Contract`.
|
/// Declare and instantiate `Budget`.
|
||||||
NewContract(Contract),
|
NewBudget(i64, Budget),
|
||||||
|
|
||||||
/// Tell a payment plan acknowledge the given `DateTime` has past.
|
/// Tell a payment plan acknowledge the given `DateTime` has past.
|
||||||
ApplyTimestamp(DateTime<Utc>),
|
ApplyTimestamp(DateTime<Utc>),
|
||||||
|
|
||||||
/// Tell the payment plan that the `NewContract` with `Signature` has been
|
/// Tell the budget that the `NewBudget` with `Signature` has been
|
||||||
/// signed by the containing transaction's `Pubkey`.
|
/// signed by the containing transaction's `Pubkey`.
|
||||||
ApplySignature,
|
ApplySignature,
|
||||||
|
|
||||||
|
|
|
@ -112,17 +112,17 @@ impl BudgetState {
|
||||||
trace!("source is pending");
|
trace!("source is pending");
|
||||||
return Err(BudgetError::SourceIsPendingContract);
|
return Err(BudgetError::SourceIsPendingContract);
|
||||||
}
|
}
|
||||||
if let Instruction::NewContract(contract) = &instruction {
|
if let Instruction::NewBudget(tokens, _) = instruction {
|
||||||
if contract.tokens < 0 {
|
if *tokens < 0 {
|
||||||
trace!("negative tokens");
|
trace!("negative tokens");
|
||||||
return Err(BudgetError::NegativeTokens);
|
return Err(BudgetError::NegativeTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
if accounts[0].tokens < contract.tokens {
|
if accounts[0].tokens < *tokens {
|
||||||
trace!("insufficient funds");
|
trace!("insufficient funds");
|
||||||
return Err(BudgetError::InsufficientFunds);
|
return Err(BudgetError::InsufficientFunds);
|
||||||
} else {
|
} else {
|
||||||
accounts[0].tokens -= contract.tokens;
|
accounts[0].tokens -= *tokens;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,8 @@ impl BudgetState {
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
) -> Result<(), BudgetError> {
|
) -> Result<(), BudgetError> {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::NewContract(contract) => {
|
Instruction::NewBudget(tokens, budget) => {
|
||||||
let budget = contract.budget.clone();
|
let budget = budget.clone();
|
||||||
if let Some(payment) = budget.final_payment() {
|
if let Some(payment) = budget.final_payment() {
|
||||||
accounts[1].tokens += payment.tokens;
|
accounts[1].tokens += payment.tokens;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -151,7 +151,7 @@ impl BudgetState {
|
||||||
} else {
|
} else {
|
||||||
let mut state = BudgetState::default();
|
let mut state = BudgetState::default();
|
||||||
state.pending_budget = Some(budget);
|
state.pending_budget = Some(budget);
|
||||||
accounts[1].tokens += contract.tokens;
|
accounts[1].tokens += tokens;
|
||||||
state.initialized = true;
|
state.initialized = true;
|
||||||
state.serialize(&mut accounts[1].userdata)
|
state.serialize(&mut accounts[1].userdata)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use bincode::{deserialize, serialize};
|
use bincode::{deserialize, serialize};
|
||||||
use budget::{Budget, Condition};
|
use budget::{Budget, Condition};
|
||||||
use budget_instruction::{Contract, Instruction, Vote};
|
use budget_instruction::{Instruction, Vote};
|
||||||
use budget_program::BudgetState;
|
use budget_program::BudgetState;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
|
@ -81,7 +81,7 @@ impl BudgetTransaction for Transaction {
|
||||||
to,
|
to,
|
||||||
};
|
};
|
||||||
let budget = Budget::Pay(payment);
|
let budget = Budget::Pay(payment);
|
||||||
let instruction = Instruction::NewContract(Contract { budget, tokens });
|
let instruction = Instruction::NewBudget(tokens, budget);
|
||||||
let userdata = serialize(&instruction).unwrap();
|
let userdata = serialize(&instruction).unwrap();
|
||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -162,7 +162,7 @@ impl BudgetTransaction for Transaction {
|
||||||
} else {
|
} else {
|
||||||
Budget::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to })
|
Budget::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to })
|
||||||
};
|
};
|
||||||
let instruction = Instruction::NewContract(Contract { budget, tokens });
|
let instruction = Instruction::NewBudget(tokens, budget);
|
||||||
let userdata = serialize(&instruction).expect("serialize instruction");
|
let userdata = serialize(&instruction).expect("serialize instruction");
|
||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -191,7 +191,7 @@ impl BudgetTransaction for Transaction {
|
||||||
} else {
|
} else {
|
||||||
Budget::After(Condition::Signature(witness), Payment { tokens, to })
|
Budget::After(Condition::Signature(witness), Payment { tokens, to })
|
||||||
};
|
};
|
||||||
let instruction = Instruction::NewContract(Contract { budget, tokens });
|
let instruction = Instruction::NewBudget(tokens, budget);
|
||||||
let userdata = serialize(&instruction).expect("serialize instruction");
|
let userdata = serialize(&instruction).expect("serialize instruction");
|
||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
|
@ -220,11 +220,8 @@ impl BudgetTransaction for Transaction {
|
||||||
/// Verify only the payment plan.
|
/// Verify only the payment plan.
|
||||||
fn verify_plan(&self) -> bool {
|
fn verify_plan(&self) -> bool {
|
||||||
for pix in 0..self.instructions.len() {
|
for pix in 0..self.instructions.len() {
|
||||||
if let Some(Instruction::NewContract(contract)) = self.instruction(pix) {
|
if let Some(Instruction::NewBudget(tokens, budget)) = self.instruction(pix) {
|
||||||
if !(self.fee >= 0
|
if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) {
|
||||||
&& self.fee <= contract.tokens
|
|
||||||
&& contract.budget.verify(contract.tokens - self.fee))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +271,7 @@ mod tests {
|
||||||
tokens: 0,
|
tokens: 0,
|
||||||
to: Default::default(),
|
to: Default::default(),
|
||||||
});
|
});
|
||||||
let instruction = Instruction::NewContract(Contract { budget, tokens: 0 });
|
let instruction = Instruction::NewBudget(0, budget);
|
||||||
let userdata = serialize(&instruction).unwrap();
|
let userdata = serialize(&instruction).unwrap();
|
||||||
let instructions = vec![transaction::Instruction {
|
let instructions = vec![transaction::Instruction {
|
||||||
program_ids_index: 0,
|
program_ids_index: 0,
|
||||||
|
@ -301,10 +298,10 @@ mod tests {
|
||||||
let pubkey = keypair.pubkey();
|
let pubkey = keypair.pubkey();
|
||||||
let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero);
|
let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero);
|
||||||
let mut instruction = tx.instruction(0).unwrap();
|
let mut instruction = tx.instruction(0).unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
if let Instruction::NewBudget(ref mut tokens, ref mut budget) = instruction {
|
||||||
contract.tokens = 1_000_000; // <-- attack, part 1!
|
*tokens = 1_000_000; // <-- attack, part 1!
|
||||||
if let Budget::Pay(ref mut payment) = contract.budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = contract.tokens; // <-- attack, part 2!
|
payment.tokens = *tokens; // <-- attack, part 2!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
||||||
|
@ -321,8 +318,8 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
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(0);
|
let mut instruction = tx.instruction(0);
|
||||||
if let Some(Instruction::NewContract(ref mut contract)) = instruction {
|
if let Some(Instruction::NewBudget(_, ref mut budget)) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = contract.budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.to = thief_keypair.pubkey(); // <-- attack!
|
payment.to = thief_keypair.pubkey(); // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,8 +335,8 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
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(0).unwrap();
|
let mut instruction = tx.instruction(0).unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
if let Instruction::NewBudget(_, ref mut budget) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = contract.budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = 2; // <-- attack!
|
payment.tokens = 2; // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,8 +345,8 @@ 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(0).unwrap();
|
let mut instruction = tx.instruction(0).unwrap();
|
||||||
if let Instruction::NewContract(ref mut contract) = instruction {
|
if let Instruction::NewBudget(_, ref mut budget) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = contract.budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = 0; // <-- whoops!
|
payment.tokens = 0; // <-- whoops!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue