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:
		| @@ -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! | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user