Add SystemProgram::Move ix to Budget tx
This commit is contained in:
parent
03ac5a6eef
commit
2045091c4f
|
@ -503,6 +503,10 @@ mod test {
|
||||||
let tx = Transaction::budget_new(&keypair, to, 192, Hash::default());
|
let tx = Transaction::budget_new(&keypair, to, 192, Hash::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.userdata(0).to_vec(),
|
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![
|
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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 1, 1, 1
|
||||||
|
|
|
@ -7,9 +7,10 @@ use budget_program::BudgetState;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use payment_plan::Payment;
|
use payment_plan::Payment;
|
||||||
use signature::Keypair;
|
use signature::{Keypair, KeypairUtil};
|
||||||
use solana_program_interface::pubkey::Pubkey;
|
use solana_program_interface::pubkey::Pubkey;
|
||||||
use transaction::Transaction;
|
use system_program::SystemProgram;
|
||||||
|
use transaction::{self, Transaction};
|
||||||
|
|
||||||
pub trait BudgetTransaction {
|
pub trait BudgetTransaction {
|
||||||
fn budget_new_taxed(
|
fn budget_new_taxed(
|
||||||
|
@ -63,8 +64,9 @@ pub trait BudgetTransaction {
|
||||||
fn vote(&self) -> Option<(Pubkey, Vote, Hash)>;
|
fn vote(&self) -> Option<(Pubkey, Vote, Hash)>;
|
||||||
|
|
||||||
fn instruction(&self, program_index: usize) -> Option<Instruction>;
|
fn instruction(&self, program_index: usize) -> Option<Instruction>;
|
||||||
|
fn system_instruction(&self, program_index: usize) -> Option<SystemProgram>;
|
||||||
|
|
||||||
fn verify_plan(&self, tokens: i64) -> bool;
|
fn verify_plan(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BudgetTransaction for Transaction {
|
impl BudgetTransaction for Transaction {
|
||||||
|
@ -76,21 +78,35 @@ impl BudgetTransaction for Transaction {
|
||||||
fee: i64,
|
fee: i64,
|
||||||
last_id: Hash,
|
last_id: Hash,
|
||||||
) -> Self {
|
) -> 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 {
|
let payment = Payment {
|
||||||
tokens: tokens - fee,
|
tokens: tokens - fee,
|
||||||
to,
|
to,
|
||||||
};
|
};
|
||||||
let budget = Budget::Pay(payment);
|
let budget_instruction = Instruction::NewBudget(Budget::Pay(payment));
|
||||||
let instruction = Instruction::NewBudget(budget);
|
let pay_userdata = serialize(&budget_instruction).unwrap();
|
||||||
let userdata = serialize(&instruction).unwrap();
|
|
||||||
Self::new(
|
let program_ids = vec![SystemProgram::id(), BudgetState::id()];
|
||||||
from_keypair,
|
|
||||||
&[to],
|
let instructions = vec![
|
||||||
BudgetState::id(),
|
transaction::Instruction {
|
||||||
userdata,
|
program_ids_index: 0,
|
||||||
last_id,
|
userdata: move_userdata,
|
||||||
fee,
|
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.
|
/// 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<Instruction> {
|
fn instruction(&self, instruction_index: usize) -> Option<Instruction> {
|
||||||
deserialize(&self.userdata(program_index)).ok()
|
deserialize(&self.userdata(instruction_index)).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_instruction(&self, instruction_index: usize) -> Option<SystemProgram> {
|
||||||
|
deserialize(&self.userdata(instruction_index)).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify only the payment plan.
|
/// Verify only the payment plan.
|
||||||
fn verify_plan(&self, tokens: i64) -> bool {
|
fn verify_plan(&self) -> bool {
|
||||||
for pix in 0..self.instructions.len() {
|
if let Some(SystemProgram::Move { tokens }) = self.system_instruction(0) {
|
||||||
if let Some(Instruction::NewBudget(budget)) = self.instruction(pix) {
|
if let Some(Instruction::NewBudget(budget)) = self.instruction(1) {
|
||||||
if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) {
|
if !(self.fee >= 0 && self.fee <= tokens && budget.verify(tokens - self.fee)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -240,10 +260,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_claim() {
|
fn test_claim() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let tokens = 42;
|
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), tokens, zero);
|
let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), 42, zero);
|
||||||
assert!(tx0.verify_plan(tokens));
|
assert!(tx0.verify_plan());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -252,9 +271,8 @@ mod tests {
|
||||||
let keypair0 = Keypair::new();
|
let keypair0 = Keypair::new();
|
||||||
let keypair1 = Keypair::new();
|
let keypair1 = Keypair::new();
|
||||||
let pubkey1 = keypair1.pubkey();
|
let pubkey1 = keypair1.pubkey();
|
||||||
let tokens = 42;
|
let tx0 = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
|
||||||
let tx0 = Transaction::budget_new(&keypair0, pubkey1, tokens, zero);
|
assert!(tx0.verify_plan());
|
||||||
assert!(tx0.verify_plan(tokens));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -262,17 +280,9 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let keypair0 = Keypair::new();
|
let keypair0 = Keypair::new();
|
||||||
let pubkey1 = Keypair::new().pubkey();
|
let pubkey1 = Keypair::new().pubkey();
|
||||||
let tokens = 1;
|
assert!(Transaction::budget_new_taxed(&keypair0, pubkey1, 1, 1, zero).verify_plan());
|
||||||
assert!(
|
assert!(!Transaction::budget_new_taxed(&keypair0, pubkey1, 1, 2, zero).verify_plan());
|
||||||
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, tokens, 2, zero).verify_plan(tokens)
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
!Transaction::budget_new_taxed(&keypair0, pubkey1, tokens, -1, zero)
|
|
||||||
.verify_plan(tokens)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -306,16 +316,15 @@ mod tests {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let pubkey = keypair.pubkey();
|
let pubkey = keypair.pubkey();
|
||||||
let tokens = 42;
|
let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero);
|
||||||
let mut tx = Transaction::budget_new(&keypair, pubkey, tokens, zero);
|
let mut instruction = tx.instruction(1).unwrap();
|
||||||
let mut instruction = tx.instruction(0).unwrap();
|
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut budget) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = 1_000_000; // <-- attack!
|
payment.tokens = 1_000_000; // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
tx.instructions[1].userdata = serialize(&instruction).unwrap();
|
||||||
assert!(!tx.verify_plan(tokens));
|
assert!(!tx.verify_plan());
|
||||||
assert!(!tx.verify_signature());
|
assert!(!tx.verify_signature());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,17 +334,16 @@ mod tests {
|
||||||
let keypair1 = Keypair::new();
|
let keypair1 = Keypair::new();
|
||||||
let thief_keypair = Keypair::new();
|
let thief_keypair = Keypair::new();
|
||||||
let pubkey1 = keypair1.pubkey();
|
let pubkey1 = keypair1.pubkey();
|
||||||
let tokens = 42;
|
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let mut tx = Transaction::budget_new(&keypair0, pubkey1, tokens, zero);
|
let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
|
||||||
let mut instruction = tx.instruction(0);
|
let mut instruction = tx.instruction(1);
|
||||||
if let Some(Instruction::NewBudget(ref mut budget)) = instruction {
|
if let Some(Instruction::NewBudget(ref mut budget)) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.to = thief_keypair.pubkey(); // <-- attack!
|
payment.to = thief_keypair.pubkey(); // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
tx.instructions[1].userdata = serialize(&instruction).unwrap();
|
||||||
assert!(tx.verify_plan(tokens));
|
assert!(tx.verify_plan());
|
||||||
assert!(!tx.verify_signature());
|
assert!(!tx.verify_signature());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,26 +351,25 @@ mod tests {
|
||||||
fn test_overspend_attack() {
|
fn test_overspend_attack() {
|
||||||
let keypair0 = Keypair::new();
|
let keypair0 = Keypair::new();
|
||||||
let keypair1 = Keypair::new();
|
let keypair1 = Keypair::new();
|
||||||
let tokens = 1;
|
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), tokens, zero);
|
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
|
||||||
let mut instruction = tx.instruction(0).unwrap();
|
let mut instruction = tx.instruction(1).unwrap();
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut budget) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = 2; // <-- attack!
|
payment.tokens = 2; // <-- attack!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
tx.instructions[1].userdata = serialize(&instruction).unwrap();
|
||||||
assert!(!tx.verify_plan(tokens));
|
assert!(!tx.verify_plan());
|
||||||
|
|
||||||
// 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(1).unwrap();
|
||||||
if let Instruction::NewBudget(ref mut budget) = instruction {
|
if let Instruction::NewBudget(ref mut budget) = instruction {
|
||||||
if let Budget::Pay(ref mut payment) = budget {
|
if let Budget::Pay(ref mut payment) = budget {
|
||||||
payment.tokens = 0; // <-- whoops!
|
payment.tokens = 0; // <-- whoops!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.instructions[0].userdata = serialize(&instruction).unwrap();
|
tx.instructions[1].userdata = serialize(&instruction).unwrap();
|
||||||
assert!(!tx.verify_plan(tokens));
|
assert!(!tx.verify_plan());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue