diff --git a/src/bank.rs b/src/bank.rs index a7fdd94694..ebd157dd2c 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -6,6 +6,7 @@ use bincode::deserialize; use bincode::serialize; use budget_program::BudgetState; +use budget_transaction::BudgetTransaction; use counter::Counter; use dynamic_program::{DynamicProgram, KeyedAccount}; use entry::Entry; diff --git a/src/banking_stage.rs b/src/banking_stage.rs index a1d2d0fff4..9b834bcbef 100644 --- a/src/banking_stage.rs +++ b/src/banking_stage.rs @@ -4,6 +4,7 @@ use bank::Bank; use bincode::deserialize; +use budget_transaction::BudgetTransaction; use counter::Counter; use entry::Entry; use hash::Hasher; diff --git a/src/budget_program.rs b/src/budget_program.rs index 39d39b9b0b..49203ab852 100644 --- a/src/budget_program.rs +++ b/src/budget_program.rs @@ -268,10 +268,12 @@ mod test { use bank::Account; use bincode::serialize; use budget_program::{BudgetError, BudgetState}; + use budget_transaction::BudgetTransaction; use chrono::prelude::{DateTime, NaiveDate, Utc}; use hash::Hash; use signature::{GenKeys, Keypair, KeypairUtil, Pubkey}; - use transaction::{BudgetTransaction, Transaction}; + use transaction::Transaction; + #[test] fn test_serializer() { let mut a = Account::new(0, 512, BudgetState::id()); diff --git a/src/budget_transaction.rs b/src/budget_transaction.rs new file mode 100644 index 0000000000..5dd8f3cc38 --- /dev/null +++ b/src/budget_transaction.rs @@ -0,0 +1,346 @@ +//! The `budget_transaction` module provides functionality for creating Budget transactions. + +use bincode::{deserialize, serialize}; +use budget::{Budget, Condition}; +use budget_program::BudgetState; +use chrono::prelude::*; +use hash::Hash; +use instruction::{Contract, Instruction, Vote}; +use payment_plan::Payment; +use signature::{Keypair, Pubkey}; +use transaction::Transaction; + +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; + + fn vote(&self) -> Option<(Pubkey, Vote, Hash)>; + + fn instruction(&self) -> Option; + + fn verify_plan(&self) -> bool; +} + +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, + ) + } + + fn vote(&self) -> Option<(Pubkey, Vote, Hash)> { + if let Some(Instruction::NewVote(vote)) = self.instruction() { + Some((*self.from(), vote, self.last_id)) + } else { + None + } + } + + fn instruction(&self) -> Option { + deserialize(&self.userdata).ok() + } + + /// Verify only the payment plan. + 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 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bincode::{deserialize, serialize}; + use signature::KeypairUtil; + + #[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()); + } +} diff --git a/src/entry.rs b/src/entry.rs index 16bc955a4b..e6930f92e5 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -3,6 +3,7 @@ //! transactions within it. Entries cannot be reordered, and its field `num_hashes` //! represents an approximate amount of time since the last Entry was created. use bincode::{serialize_into, serialized_size}; +use budget_transaction::BudgetTransaction; use hash::Hash; use packet::{BlobRecycler, SharedBlob, BLOB_DATA_SIZE}; use poh::Poh; @@ -226,12 +227,13 @@ pub fn next_entry(start_hash: &Hash, num_hashes: u64, transactions: Vec Vec<(Pubkey, Vote, Hash)> { self.iter() - .flat_map(|entry| entry.transactions.iter().filter_map(Transaction::vote)) - .collect() + .flat_map(|entry| { + entry + .transactions + .iter() + .filter_map(BudgetTransaction::vote) + }).collect() } } @@ -576,6 +581,7 @@ pub fn genesis(name: &str, num: i64) -> (Mint, String) { mod tests { use super::*; use bincode::serialized_size; + use budget_transaction::BudgetTransaction; use chrono::prelude::*; use entry::{next_entry, Entry}; use hash::hash; @@ -584,7 +590,7 @@ mod tests { use signature::{Keypair, KeypairUtil}; use std; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - use transaction::{BudgetTransaction, Transaction}; + use transaction::Transaction; #[test] fn test_verify_slice() { diff --git a/src/lib.rs b/src/lib.rs index aba65f3aa0..c2f66f08b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ pub mod banking_stage; pub mod blob_fetch_stage; pub mod broadcast_stage; pub mod budget; +pub mod budget_transaction; pub mod choose_gossip_peer_strategy; pub mod client; pub mod instruction; diff --git a/src/transaction.rs b/src/transaction.rs index f2cb74dd15..bc0134ec09 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,12 +1,7 @@ //! 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 bincode::serialize; use hash::{Hash, Hasher}; -use instruction::{Contract, Instruction, Vote}; -use payment_plan::Payment; use signature::{Keypair, KeypairUtil, Pubkey, Signature}; use std::mem::size_of; @@ -102,29 +97,10 @@ impl Transaction { .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(); @@ -135,311 +111,12 @@ impl Transaction { } } -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 bincode::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] diff --git a/src/vote_stage.rs b/src/vote_stage.rs index c2e9c57717..a5d7e8c50c 100644 --- a/src/vote_stage.rs +++ b/src/vote_stage.rs @@ -2,6 +2,7 @@ use bank::Bank; use bincode::serialize; +use budget_transaction::BudgetTransaction; use counter::Counter; use crdt::Crdt; use hash::Hash; @@ -16,7 +17,7 @@ use std::sync::atomic::AtomicUsize; use std::sync::{Arc, RwLock}; use streamer::BlobSender; use timing; -use transaction::{BudgetTransaction, Transaction}; +use transaction::Transaction; pub const VOTE_TIMEOUT_MS: u64 = 1000; diff --git a/src/wallet.rs b/src/wallet.rs index 56dc0f6276..bc8b95f8d6 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,6 +1,7 @@ use bincode::{deserialize, serialize}; use bs58; use budget_program::BudgetState; +use budget_transaction::BudgetTransaction; use chrono::prelude::*; use clap::ArgMatches; use crdt::NodeInfo; @@ -23,7 +24,7 @@ use std::thread::sleep; use std::time::Duration; use std::{error, fmt, mem}; use system_transaction::SystemTransaction; -use transaction::{BudgetTransaction, Transaction}; +use transaction::Transaction; #[derive(Debug, PartialEq)] pub enum WalletCommand {