From 2045091c4f8033046d48524dac67e27b31ff6404 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 17 Oct 2018 23:21:19 -0600 Subject: [PATCH] Add SystemProgram::Move ix to Budget tx --- src/budget_program.rs | 4 ++ src/budget_transaction.rs | 115 ++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/src/budget_program.rs b/src/budget_program.rs index fa1b5d1c60..e4dcbec146 100644 --- a/src/budget_program.rs +++ b/src/budget_program.rs @@ -503,6 +503,10 @@ mod test { let tx = Transaction::budget_new(&keypair, to, 192, Hash::default()); assert_eq!( tx.userdata(0).to_vec(), + vec![2, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0] + ); + assert_eq!( + tx.userdata(1).to_vec(), vec![ 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 diff --git a/src/budget_transaction.rs b/src/budget_transaction.rs index a63cc70708..1fcec3d342 100644 --- a/src/budget_transaction.rs +++ b/src/budget_transaction.rs @@ -7,9 +7,10 @@ use budget_program::BudgetState; use chrono::prelude::*; use hash::Hash; use payment_plan::Payment; -use signature::Keypair; +use signature::{Keypair, KeypairUtil}; use solana_program_interface::pubkey::Pubkey; -use transaction::Transaction; +use system_program::SystemProgram; +use transaction::{self, Transaction}; pub trait BudgetTransaction { fn budget_new_taxed( @@ -63,8 +64,9 @@ pub trait BudgetTransaction { fn vote(&self) -> Option<(Pubkey, Vote, Hash)>; fn instruction(&self, program_index: usize) -> Option; + fn system_instruction(&self, program_index: usize) -> Option; - fn verify_plan(&self, tokens: i64) -> bool; + fn verify_plan(&self) -> bool; } impl BudgetTransaction for Transaction { @@ -76,21 +78,35 @@ impl BudgetTransaction for Transaction { fee: i64, last_id: Hash, ) -> Self { + let contract = Keypair::new().pubkey(); + let keys = vec![from_keypair.pubkey(), contract]; + + let system_instruction = SystemProgram::Move { tokens }; + let move_userdata = serialize(&system_instruction).unwrap(); + let payment = Payment { tokens: tokens - fee, to, }; - let budget = Budget::Pay(payment); - let instruction = Instruction::NewBudget(budget); - let userdata = serialize(&instruction).unwrap(); - Self::new( - from_keypair, - &[to], - BudgetState::id(), - userdata, - last_id, - fee, - ) + let budget_instruction = Instruction::NewBudget(Budget::Pay(payment)); + let pay_userdata = serialize(&budget_instruction).unwrap(); + + let program_ids = vec![SystemProgram::id(), BudgetState::id()]; + + let instructions = vec![ + transaction::Instruction { + program_ids_index: 0, + userdata: move_userdata, + accounts: vec![0, 1], + }, + transaction::Instruction { + program_ids_index: 1, + userdata: pay_userdata, + accounts: vec![1], + }, + ]; + + Self::new_with_instructions(from_keypair, &keys, last_id, fee, program_ids, instructions) } /// Create and sign a new Transaction. Used for unit-testing. @@ -213,14 +229,18 @@ impl BudgetTransaction for Transaction { } } - fn instruction(&self, program_index: usize) -> Option { - deserialize(&self.userdata(program_index)).ok() + fn instruction(&self, instruction_index: usize) -> Option { + deserialize(&self.userdata(instruction_index)).ok() + } + + fn system_instruction(&self, instruction_index: usize) -> Option { + deserialize(&self.userdata(instruction_index)).ok() } /// Verify only the payment plan. - fn verify_plan(&self, tokens: i64) -> bool { - for pix in 0..self.instructions.len() { - if let Some(Instruction::NewBudget(budget)) = self.instruction(pix) { + fn verify_plan(&self) -> bool { + if let Some(SystemProgram::Move { tokens }) = self.system_instruction(0) { + if let Some(Instruction::NewBudget(budget)) = self.instruction(1) { if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) { return false; } @@ -240,10 +260,9 @@ mod tests { #[test] fn test_claim() { let keypair = Keypair::new(); - let tokens = 42; let zero = Hash::default(); - let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), tokens, zero); - assert!(tx0.verify_plan(tokens)); + let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), 42, zero); + assert!(tx0.verify_plan()); } #[test] @@ -252,9 +271,8 @@ mod tests { let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); let pubkey1 = keypair1.pubkey(); - let tokens = 42; - let tx0 = Transaction::budget_new(&keypair0, pubkey1, tokens, zero); - assert!(tx0.verify_plan(tokens)); + let tx0 = Transaction::budget_new(&keypair0, pubkey1, 42, zero); + assert!(tx0.verify_plan()); } #[test] @@ -262,17 +280,9 @@ mod tests { let zero = Hash::default(); let keypair0 = Keypair::new(); let pubkey1 = Keypair::new().pubkey(); - let tokens = 1; - assert!( - Transaction::budget_new_taxed(&keypair0, pubkey1, tokens, 1, zero).verify_plan(tokens) - ); - assert!( - !Transaction::budget_new_taxed(&keypair0, pubkey1, tokens, 2, zero).verify_plan(tokens) - ); - assert!( - !Transaction::budget_new_taxed(&keypair0, pubkey1, tokens, -1, zero) - .verify_plan(tokens) - ); + assert!(Transaction::budget_new_taxed(&keypair0, pubkey1, 1, 1, zero).verify_plan()); + assert!(!Transaction::budget_new_taxed(&keypair0, pubkey1, 1, 2, zero).verify_plan()); + assert!(!Transaction::budget_new_taxed(&keypair0, pubkey1, 1, -1, zero).verify_plan()); } #[test] @@ -306,16 +316,15 @@ mod tests { let zero = Hash::default(); let keypair = Keypair::new(); let pubkey = keypair.pubkey(); - let tokens = 42; - let mut tx = Transaction::budget_new(&keypair, pubkey, tokens, zero); - let mut instruction = tx.instruction(0).unwrap(); + let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero); + let mut instruction = tx.instruction(1).unwrap(); if let Instruction::NewBudget(ref mut budget) = instruction { if let Budget::Pay(ref mut payment) = budget { payment.tokens = 1_000_000; // <-- attack! } } - tx.instructions[0].userdata = serialize(&instruction).unwrap(); - assert!(!tx.verify_plan(tokens)); + tx.instructions[1].userdata = serialize(&instruction).unwrap(); + assert!(!tx.verify_plan()); assert!(!tx.verify_signature()); } @@ -325,17 +334,16 @@ mod tests { let keypair1 = Keypair::new(); let thief_keypair = Keypair::new(); let pubkey1 = keypair1.pubkey(); - let tokens = 42; let zero = Hash::default(); - let mut tx = Transaction::budget_new(&keypair0, pubkey1, tokens, zero); - let mut instruction = tx.instruction(0); + let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero); + let mut instruction = tx.instruction(1); if let Some(Instruction::NewBudget(ref mut budget)) = instruction { if let Budget::Pay(ref mut payment) = budget { payment.to = thief_keypair.pubkey(); // <-- attack! } } - tx.instructions[0].userdata = serialize(&instruction).unwrap(); - assert!(tx.verify_plan(tokens)); + tx.instructions[1].userdata = serialize(&instruction).unwrap(); + assert!(tx.verify_plan()); assert!(!tx.verify_signature()); } @@ -343,26 +351,25 @@ mod tests { fn test_overspend_attack() { let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); - let tokens = 1; let zero = Hash::default(); - let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), tokens, zero); - let mut instruction = tx.instruction(0).unwrap(); + let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero); + let mut instruction = tx.instruction(1).unwrap(); if let Instruction::NewBudget(ref mut budget) = instruction { if let Budget::Pay(ref mut payment) = budget { payment.tokens = 2; // <-- attack! } } - tx.instructions[0].userdata = serialize(&instruction).unwrap(); - assert!(!tx.verify_plan(tokens)); + tx.instructions[1].userdata = serialize(&instruction).unwrap(); + assert!(!tx.verify_plan()); // Also, ensure all branchs of the plan spend all tokens - let mut instruction = tx.instruction(0).unwrap(); + let mut instruction = tx.instruction(1).unwrap(); if let Instruction::NewBudget(ref mut budget) = instruction { if let Budget::Pay(ref mut payment) = budget { payment.tokens = 0; // <-- whoops! } } - tx.instructions[0].userdata = serialize(&instruction).unwrap(); - assert!(!tx.verify_plan(tokens)); + tx.instructions[1].userdata = serialize(&instruction).unwrap(); + assert!(!tx.verify_plan()); } }