//! The `transaction` module provides functionality for creating log transactions. use bincode::{deserialize, serialize}; use budget::{Budget, Condition}; use budget_program::BudgetState; use chrono::prelude::*; use hash::{Hash, Hasher}; use instruction::{Contract, Instruction, Vote}; use payment_plan::Payment; use signature::{Keypair, KeypairUtil, Pubkey, Signature}; use std::mem::size_of; pub const SIGNED_DATA_OFFSET: usize = size_of::(); pub const SIG_OFFSET: usize = 0; pub const PUB_KEY_OFFSET: usize = size_of::() + size_of::(); /// An instruction signed by a client with `Pubkey`. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Transaction { /// A digital signature of `keys`, `program_id`, `last_id`, `fee` and `userdata`, signed by `Pubkey`. pub signature: Signature, /// The `Pubkeys` that are executing this transaction userdata. The meaning of each key is /// program-specific. /// * keys[0] - Typically this is the `caller` public key. `signature` is verified with keys[0]. /// In the future which key pays the fee and which keys have signatures would be configurable. /// * keys[1] - Typically this is the program context or the recipient of the tokens pub keys: Vec, /// The program code that executes this transaction is identified by the program_id. pub program_id: Pubkey, /// The ID of a recent ledger entry. pub last_id: Hash, /// The number of tokens paid for processing and storage of this transaction. pub fee: i64, /// Userdata to be stored in the account pub userdata: Vec, } impl Transaction { /// Create a signed transaction from the given `Instruction`. /// * `from_keypair` - The key used to sign the transaction. This key is stored as keys[0] /// * `transaction_keys` - The keys for the transaction. These are the program state /// instances or token recipient keys. /// * `userdata` - The input data that the program will execute with /// * `last_id` - The PoH hash. /// * `fee` - The transaction fee. pub fn new( from_keypair: &Keypair, transaction_keys: &[Pubkey], program_id: Pubkey, userdata: Vec, last_id: Hash, fee: i64, ) -> Self { let from = from_keypair.pubkey(); let mut keys = vec![from]; keys.extend_from_slice(transaction_keys); let mut tx = Transaction { signature: Signature::default(), keys, program_id, last_id, fee, userdata, }; tx.sign(from_keypair); tx } /// Get the transaction data to sign. pub fn get_sign_data(&self) -> Vec { let mut data = serialize(&(&self.keys)).expect("serialize keys"); let program_id = serialize(&(&self.program_id)).expect("serialize program_id"); data.extend_from_slice(&program_id); let last_id_data = serialize(&(&self.last_id)).expect("serialize last_id"); data.extend_from_slice(&last_id_data); let fee_data = serialize(&(&self.fee)).expect("serialize last_id"); data.extend_from_slice(&fee_data); let userdata = serialize(&(&self.userdata)).expect("serialize userdata"); data.extend_from_slice(&userdata); data } /// Sign this transaction. pub fn sign(&mut self, keypair: &Keypair) { let sign_data = self.get_sign_data(); self.signature = Signature::new(keypair.sign(&sign_data).as_ref()); } /// Verify only the transaction signature. pub fn verify_signature(&self) -> bool { warn!("transaction signature verification called"); self.signature .verify(&self.from().as_ref(), &self.get_sign_data()) } pub fn vote(&self) -> Option<(Pubkey, Vote, Hash)> { if let Some(Instruction::NewVote(vote)) = self.instruction() { Some((*self.from(), vote, self.last_id)) } else { None } } pub fn from(&self) -> &Pubkey { &self.keys[0] } pub fn instruction(&self) -> Option { deserialize(&self.userdata).ok() } /// Verify only the payment plan. pub fn verify_plan(&self) -> bool { if let Some(Instruction::NewContract(contract)) = self.instruction() { self.fee >= 0 && self.fee <= contract.tokens && contract.budget.verify(contract.tokens - self.fee) } else { true } } // a hash of a slice of transactions only needs to hash the signatures pub fn hash(transactions: &[Transaction]) -> Hash { let mut hasher = Hasher::default(); transactions .iter() .for_each(|tx| hasher.hash(&tx.signature.as_ref())); hasher.result() } } pub trait BudgetTransaction { fn budget_new_taxed( from_keypair: &Keypair, to: Pubkey, tokens: i64, fee: i64, last_id: Hash, ) -> Self; fn budget_new(from_keypair: &Keypair, to: Pubkey, tokens: i64, last_id: Hash) -> Self; fn budget_new_timestamp( from_keypair: &Keypair, contract: Pubkey, to: Pubkey, dt: DateTime, last_id: Hash, ) -> Self; fn budget_new_signature( from_keypair: &Keypair, contract: Pubkey, to: Pubkey, last_id: Hash, ) -> Self; fn budget_new_vote(from_keypair: &Keypair, vote: Vote, last_id: Hash, fee: i64) -> Self; fn budget_new_on_date( from_keypair: &Keypair, to: Pubkey, contract: Pubkey, dt: DateTime, dt_pubkey: Pubkey, cancelable: Option, tokens: i64, last_id: Hash, ) -> Self; fn budget_new_when_signed( from_keypair: &Keypair, to: Pubkey, contract: Pubkey, witness: Pubkey, cancelable: Option, tokens: i64, last_id: Hash, ) -> Self; } impl BudgetTransaction for Transaction { /// Create and sign a new Transaction. Used for unit-testing. fn budget_new_taxed( from_keypair: &Keypair, to: Pubkey, tokens: i64, fee: i64, last_id: Hash, ) -> Self { let payment = Payment { tokens: tokens - fee, to, }; let budget = Budget::Pay(payment); let instruction = Instruction::NewContract(Contract { budget, tokens }); let userdata = serialize(&instruction).unwrap(); Self::new( from_keypair, &[to], BudgetState::id(), userdata, last_id, fee, ) } /// Create and sign a new Transaction. Used for unit-testing. fn budget_new(from_keypair: &Keypair, to: Pubkey, tokens: i64, last_id: Hash) -> Self { Self::budget_new_taxed(from_keypair, to, tokens, 0, last_id) } /// Create and sign a new Witness Timestamp. Used for unit-testing. fn budget_new_timestamp( from_keypair: &Keypair, contract: Pubkey, to: Pubkey, dt: DateTime, last_id: Hash, ) -> Self { let instruction = Instruction::ApplyTimestamp(dt); let userdata = serialize(&instruction).unwrap(); Self::new( from_keypair, &[contract, to], BudgetState::id(), userdata, last_id, 0, ) } /// Create and sign a new Witness Signature. Used for unit-testing. fn budget_new_signature( from_keypair: &Keypair, contract: Pubkey, to: Pubkey, last_id: Hash, ) -> Self { let instruction = Instruction::ApplySignature; let userdata = serialize(&instruction).unwrap(); Self::new( from_keypair, &[contract, to], BudgetState::id(), userdata, last_id, 0, ) } fn budget_new_vote(from_keypair: &Keypair, vote: Vote, last_id: Hash, fee: i64) -> Self { let instruction = Instruction::NewVote(vote); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new(from_keypair, &[], BudgetState::id(), userdata, last_id, fee) } /// Create and sign a postdated Transaction. Used for unit-testing. fn budget_new_on_date( from_keypair: &Keypair, to: Pubkey, contract: Pubkey, dt: DateTime, dt_pubkey: Pubkey, cancelable: Option, tokens: i64, last_id: Hash, ) -> Self { let budget = if let Some(from) = cancelable { Budget::Or( (Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ) } else { Budget::After(Condition::Timestamp(dt, dt_pubkey), Payment { tokens, to }) }; let instruction = Instruction::NewContract(Contract { budget, tokens }); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new( from_keypair, &[contract], BudgetState::id(), userdata, last_id, 0, ) } /// Create and sign a multisig Transaction. fn budget_new_when_signed( from_keypair: &Keypair, to: Pubkey, contract: Pubkey, witness: Pubkey, cancelable: Option, tokens: i64, last_id: Hash, ) -> Self { let budget = if let Some(from) = cancelable { Budget::Or( (Condition::Signature(witness), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ) } else { Budget::After(Condition::Signature(witness), Payment { tokens, to }) }; let instruction = Instruction::NewContract(Contract { budget, tokens }); let userdata = serialize(&instruction).expect("serialize instruction"); Self::new( from_keypair, &[contract], BudgetState::id(), userdata, last_id, 0, ) } } #[cfg(test)] mod tests { use super::*; use bincode::{deserialize, serialize}; use signature::GenKeys; #[test] fn test_claim() { let keypair = Keypair::new(); let zero = Hash::default(); let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), 42, zero); assert!(tx0.verify_plan()); } #[test] fn test_transfer() { let zero = Hash::default(); let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); let pubkey1 = keypair1.pubkey(); let tx0 = Transaction::budget_new(&keypair0, pubkey1, 42, zero); assert!(tx0.verify_plan()); } #[test] fn test_transfer_with_fee() { let zero = Hash::default(); let keypair0 = Keypair::new(); let pubkey1 = Keypair::new().pubkey(); 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] fn test_serialize_claim() { let budget = Budget::Pay(Payment { tokens: 0, to: Default::default(), }); let instruction = Instruction::NewContract(Contract { budget, tokens: 0 }); let userdata = serialize(&instruction).unwrap(); let claim0 = Transaction { keys: vec![], last_id: Default::default(), signature: Default::default(), program_id: Default::default(), fee: 0, userdata, }; let buf = serialize(&claim0).unwrap(); let claim1: Transaction = deserialize(&buf).unwrap(); assert_eq!(claim1, claim0); } #[test] fn test_token_attack() { let zero = Hash::default(); let keypair = Keypair::new(); let pubkey = keypair.pubkey(); let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero); let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { contract.tokens = 1_000_000; // <-- attack, part 1! if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = contract.tokens; // <-- attack, part 2! } } tx.userdata = serialize(&instruction).unwrap(); assert!(tx.verify_plan()); assert!(!tx.verify_signature()); } #[test] fn test_hijack_attack() { let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); let thief_keypair = Keypair::new(); let pubkey1 = keypair1.pubkey(); let zero = Hash::default(); let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero); let mut instruction = tx.instruction(); if let Some(Instruction::NewContract(ref mut contract)) = instruction { if let Budget::Pay(ref mut payment) = contract.budget { payment.to = thief_keypair.pubkey(); // <-- attack! } } tx.userdata = serialize(&instruction).unwrap(); assert!(tx.verify_plan()); assert!(!tx.verify_signature()); } #[test] fn test_overspend_attack() { let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); let zero = Hash::default(); let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero); let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = 2; // <-- attack! } } tx.userdata = serialize(&instruction).unwrap(); assert!(!tx.verify_plan()); // Also, ensure all branchs of the plan spend all tokens let mut instruction = tx.instruction().unwrap(); if let Instruction::NewContract(ref mut contract) = instruction { if let Budget::Pay(ref mut payment) = contract.budget { payment.tokens = 0; // <-- whoops! } } tx.userdata = serialize(&instruction).unwrap(); assert!(!tx.verify_plan()); } /// Detect binary changes in the serialized contract userdata, which could have a downstream /// affect on SDKs and DApps #[test] fn test_sdk_serialize() { let keypair = &GenKeys::new([0u8; 32]).gen_n_keypairs(1)[0]; let to = Pubkey::new(&[ 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, ]); let program_id = Pubkey::new(&[ 2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, ]); let tx = Transaction::new( keypair, &[keypair.pubkey(), to], program_id, vec![1, 2, 3], Hash::default(), 99, ); assert_eq!( serialize(&tx).unwrap(), vec![ 88, 1, 212, 176, 31, 197, 35, 156, 135, 24, 30, 57, 204, 253, 224, 28, 89, 189, 53, 64, 27, 148, 42, 199, 43, 236, 85, 182, 150, 64, 96, 53, 255, 235, 90, 197, 228, 6, 105, 22, 140, 209, 206, 221, 85, 117, 125, 126, 11, 1, 176, 130, 57, 236, 7, 155, 127, 58, 130, 92, 230, 219, 254, 0, 3, 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, 72, 134, 137, 247, 161, 68, 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, 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, 2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 ], ); } }