diff --git a/Cargo.toml b/Cargo.toml index 0eb6f858189429..cda99ce34ad215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana" description = "The World's Fastest Blockchain" -version = "0.6.0-alpha" +version = "0.6.0-beta" documentation = "https://docs.rs/solana" homepage = "http://solana.com/" repository = "https://github.com/solana-labs/solana" diff --git a/src/bank.rs b/src/bank.rs index 02ace8844750c1..3b2ef23c98ebe2 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -15,8 +15,8 @@ use signature::{KeyPair, PublicKey, Signature}; use std::collections::hash_map::Entry::Occupied; use std::collections::{HashMap, HashSet, VecDeque}; use std::result; -use std::sync::RwLock; use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering}; +use std::sync::RwLock; use transaction::{Instruction, Plan, Transaction}; pub const MAX_ENTRY_IDS: usize = 1024 * 4; @@ -25,7 +25,9 @@ pub const MAX_ENTRY_IDS: usize = 1024 * 4; pub enum BankError { AccountNotFound(PublicKey), InsufficientFunds(PublicKey), - InvalidTransferSignature(Signature), + DuplicateSiganture(Signature), + LastIdNotFound(Hash), + NegativeTokens, } pub type Result = result::Result; @@ -94,29 +96,29 @@ impl Bank { last_item.0 } - fn reserve_signature(signatures: &RwLock>, sig: &Signature) -> bool { + fn reserve_signature(signatures: &RwLock>, sig: &Signature) -> Result<()> { if signatures .read() .expect("'signatures' read lock") .contains(sig) { - return false; + return Err(BankError::DuplicateSiganture(*sig)); } signatures .write() .expect("'signatures' write lock") .insert(*sig); - true + Ok(()) } - fn forget_signature(signatures: &RwLock>, sig: &Signature) -> bool { + fn forget_signature(signatures: &RwLock>, sig: &Signature) { signatures .write() .expect("'signatures' write lock in forget_signature") - .remove(sig) + .remove(sig); } - fn forget_signature_with_last_id(&self, sig: &Signature, last_id: &Hash) -> bool { + fn forget_signature_with_last_id(&self, sig: &Signature, last_id: &Hash) { if let Some(entry) = self.last_ids .read() .expect("'last_ids' read lock in forget_signature_with_last_id") @@ -124,12 +126,11 @@ impl Bank { .rev() .find(|x| x.0 == *last_id) { - return Self::forget_signature(&entry.1, sig); + Self::forget_signature(&entry.1, sig); } - return false; } - fn reserve_signature_with_last_id(&self, sig: &Signature, last_id: &Hash) -> bool { + fn reserve_signature_with_last_id(&self, sig: &Signature, last_id: &Hash) -> Result<()> { if let Some(entry) = self.last_ids .read() .expect("'last_ids' read lock in reserve_signature_with_last_id") @@ -139,7 +140,7 @@ impl Bank { { return Self::reserve_signature(&entry.1, sig); } - false + Err(BankError::LastIdNotFound(*last_id)) } /// Tell the bank which Entry IDs exist on the ledger. This function @@ -161,6 +162,9 @@ impl Bank { fn apply_debits(&self, tx: &Transaction) -> Result<()> { if let Instruction::NewContract(contract) = &tx.instruction { trace!("Transaction {}", contract.tokens); + if contract.tokens < 0 { + return Err(BankError::NegativeTokens); + } } let bals = self.balances .read() @@ -171,9 +175,7 @@ impl Bank { return Err(BankError::AccountNotFound(tx.from)); } - if !self.reserve_signature_with_last_id(&tx.sig, &tx.last_id) { - return Err(BankError::InvalidTransferSignature(tx.sig)); - } + self.reserve_signature_with_last_id(&tx.sig, &tx.last_id)?; loop { let result = if let Instruction::NewContract(contract) = &tx.instruction { @@ -403,6 +405,18 @@ mod tests { assert_eq!(bank.transaction_count(), 2); } + #[test] + fn test_invalid_tokens() { + let mint = Mint::new(1); + let pubkey = KeyPair::new().pubkey(); + let bank = Bank::new(&mint); + assert_eq!( + bank.transfer(-1, &mint.keypair(), pubkey, mint.last_id()), + Err(BankError::NegativeTokens) + ); + assert_eq!(bank.transaction_count(), 0); + } + #[test] fn test_account_not_found() { let mint = Mint::new(1); @@ -528,8 +542,14 @@ mod tests { let mint = Mint::new(1); let bank = Bank::new(&mint); let sig = Signature::default(); - assert!(bank.reserve_signature_with_last_id(&sig, &mint.last_id())); - assert!(!bank.reserve_signature_with_last_id(&sig, &mint.last_id())); + assert!( + bank.reserve_signature_with_last_id(&sig, &mint.last_id()) + .is_ok() + ); + assert_eq!( + bank.reserve_signature_with_last_id(&sig, &mint.last_id()), + Err(BankError::DuplicateSiganture(sig)) + ); } #[test] @@ -537,9 +557,13 @@ mod tests { let mint = Mint::new(1); let bank = Bank::new(&mint); let sig = Signature::default(); - bank.reserve_signature_with_last_id(&sig, &mint.last_id()); - assert!(bank.forget_signature_with_last_id(&sig, &mint.last_id())); - assert!(!bank.forget_signature_with_last_id(&sig, &mint.last_id())); + bank.reserve_signature_with_last_id(&sig, &mint.last_id()) + .unwrap(); + bank.forget_signature_with_last_id(&sig, &mint.last_id()); + assert!( + bank.reserve_signature_with_last_id(&sig, &mint.last_id()) + .is_ok() + ); } #[test] @@ -552,7 +576,10 @@ mod tests { bank.register_entry_id(&last_id); } // Assert we're no longer able to use the oldest entry ID. - assert!(!bank.reserve_signature_with_last_id(&sig, &mint.last_id())); + assert_eq!( + bank.reserve_signature_with_last_id(&sig, &mint.last_id()), + Err(BankError::LastIdNotFound(mint.last_id())) + ); } #[test] diff --git a/src/banking_stage.rs b/src/banking_stage.rs index 64a8fa66666fff..aed137d239666c 100644 --- a/src/banking_stage.rs +++ b/src/banking_stage.rs @@ -8,9 +8,9 @@ use rayon::prelude::*; use record_stage::Signal; use result::Result; use std::net::SocketAddr; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::Arc; use std::thread::{spawn, JoinHandle}; use std::time::Duration; use std::time::Instant; @@ -255,8 +255,8 @@ mod bench { use record_stage::Signal; use signature::{KeyPair, KeyPairUtil}; use std::iter; - use std::sync::Arc; use std::sync::mpsc::channel; + use std::sync::Arc; use transaction::Transaction; #[bench] diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index 589a158cec5c41..070fca2df8995b 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -24,8 +24,8 @@ use std::net::{IpAddr, SocketAddr, UdpSocket}; use std::process::exit; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; -use std::thread::JoinHandle; use std::thread::sleep; +use std::thread::JoinHandle; use std::time::Duration; use std::time::Instant; diff --git a/src/bin/fullnode.rs b/src/bin/fullnode.rs index d0a44615308038..8cf0dac0a64219 100644 --- a/src/bin/fullnode.rs +++ b/src/bin/fullnode.rs @@ -20,8 +20,8 @@ use std::fs::File; use std::io::{stdin, Read}; use std::net::{IpAddr, SocketAddr, UdpSocket}; use std::process::exit; -use std::sync::Arc; use std::sync::atomic::AtomicBool; +use std::sync::Arc; //use std::time::Duration; fn print_usage(program: &str, opts: Options) { diff --git a/src/entry_writer.rs b/src/entry_writer.rs index 18a37481dc4e8b..0d174b3450ea77 100644 --- a/src/entry_writer.rs +++ b/src/entry_writer.rs @@ -7,8 +7,8 @@ use packet; use result::Result; use serde_json; use std::collections::VecDeque; -use std::io::Write; use std::io::sink; +use std::io::Write; use std::sync::mpsc::Receiver; use std::sync::{Arc, Mutex}; use std::time::Duration; diff --git a/src/fetch_stage.rs b/src/fetch_stage.rs index 71e633d67b78c0..ad71882d739e5a 100644 --- a/src/fetch_stage.rs +++ b/src/fetch_stage.rs @@ -2,9 +2,9 @@ use packet; use std::net::UdpSocket; -use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::mpsc::channel; +use std::sync::Arc; use std::thread::JoinHandle; use streamer; diff --git a/src/hash.rs b/src/hash.rs index ee7598a0dc29e2..61dd01468c9fcb 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,7 +1,7 @@ //! The `hash` module provides functions for creating SHA-256 hashes. -use generic_array::GenericArray; use generic_array::typenum::U32; +use generic_array::GenericArray; use sha2::{Digest, Sha256}; pub type Hash = GenericArray; diff --git a/src/replicate_stage.rs b/src/replicate_stage.rs index 30ccf468f17a16..a3df0c4a328754 100644 --- a/src/replicate_stage.rs +++ b/src/replicate_stage.rs @@ -4,8 +4,8 @@ use bank::Bank; use ledger; use packet; use result::Result; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread::{spawn, JoinHandle}; use std::time::Duration; use streamer; diff --git a/src/request_stage.rs b/src/request_stage.rs index 33d845aab4824f..4c10659eec2192 100644 --- a/src/request_stage.rs +++ b/src/request_stage.rs @@ -8,9 +8,9 @@ use request::Request; use request_processor::RequestProcessor; use result::Result; use std::net::SocketAddr; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver}; +use std::sync::Arc; use std::thread::{spawn, JoinHandle}; use std::time::Instant; use streamer; diff --git a/src/result.rs b/src/result.rs index 56284904033c0a..e31e45e2d1da59 100644 --- a/src/result.rs +++ b/src/result.rs @@ -80,9 +80,9 @@ mod tests { use std::io::Write; use std::net::SocketAddr; use std::panic; + use std::sync::mpsc::channel; use std::sync::mpsc::RecvError; use std::sync::mpsc::RecvTimeoutError; - use std::sync::mpsc::channel; use std::thread; fn addr_parse_error() -> Result { diff --git a/src/rpu.rs b/src/rpu.rs index eef1515d21a3ad..537b75c1ce0a0b 100644 --- a/src/rpu.rs +++ b/src/rpu.rs @@ -6,9 +6,9 @@ use packet; use request_processor::RequestProcessor; use request_stage::RequestStage; use std::net::UdpSocket; -use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::mpsc::channel; +use std::sync::Arc; use std::thread::JoinHandle; use streamer; diff --git a/src/signature.rs b/src/signature.rs index 51c38eb5ccf72c..b32ccbdc3a4187 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -1,7 +1,7 @@ //! The `signature` module provides functionality for public, and private keys. -use generic_array::GenericArray; use generic_array::typenum::{U32, U64}; +use generic_array::GenericArray; use rand::{ChaChaRng, Rng, SeedableRng}; use rayon::prelude::*; use ring::error::Unspecified; diff --git a/src/streamer.rs b/src/streamer.rs index 1f84a2af30ebf9..ef5b399e6334e4 100644 --- a/src/streamer.rs +++ b/src/streamer.rs @@ -597,8 +597,8 @@ mod test { use std::sync::{Arc, RwLock}; use std::thread::sleep; use std::time::Duration; - use streamer::{default_window, BlobReceiver, PacketReceiver}; use streamer::{blob_receiver, receiver, responder, retransmitter, window}; + use streamer::{default_window, BlobReceiver, PacketReceiver}; fn get_msgs(r: PacketReceiver, num: &mut usize) { for _t in 0..5 { diff --git a/src/thin_client.rs b/src/thin_client.rs index 86e4585f630a5c..7e20682cc052cc 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -183,8 +183,8 @@ mod tests { use server::Server; use signature::{KeyPair, KeyPairUtil}; use std::io::sink; - use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; use std::thread::sleep; use std::time::Duration; use transaction::{Instruction, Plan}; diff --git a/src/transaction.rs b/src/transaction.rs index 2c342234a9d202..083e93637b7612 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -56,6 +56,7 @@ pub struct Transaction { pub from: PublicKey, pub instruction: Instruction, pub last_id: Hash, + pub fee: i64, } impl Transaction { @@ -63,6 +64,7 @@ impl Transaction { from_keypair: &KeyPair, instruction: Instruction, last_id: Hash, + fee: i64, ) -> Self { let from = from_keypair.pubkey(); let mut tx = Transaction { @@ -70,29 +72,45 @@ impl Transaction { instruction, last_id, from, + fee, }; tx.sign(from_keypair); tx } /// Create and sign a new Transaction. Used for unit-testing. - pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { - let budget = Budget::Pay(Payment { tokens, to }); + pub fn new_taxed( + from_keypair: &KeyPair, + to: PublicKey, + tokens: i64, + fee: i64, + last_id: Hash, + ) -> Self { + let payment = Payment { + tokens: tokens - fee, + to, + }; + let budget = Budget::Pay(payment); let plan = Plan::Budget(budget); let instruction = Instruction::NewContract(Contract { plan, tokens }); - Self::new_from_instruction(from_keypair, instruction, last_id) + Self::new_from_instruction(from_keypair, instruction, last_id, fee) + } + + /// Create and sign a new Transaction. Used for unit-testing. + pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { + Self::new_taxed(from_keypair, to, tokens, 0, last_id) } /// Create and sign a new Witness Timestamp. Used for unit-testing. pub fn new_timestamp(from_keypair: &KeyPair, dt: DateTime, last_id: Hash) -> Self { let instruction = Instruction::ApplyTimestamp(dt); - Self::new_from_instruction(from_keypair, instruction, last_id) + Self::new_from_instruction(from_keypair, instruction, last_id, 0) } /// Create and sign a new Witness Signature. Used for unit-testing. pub fn new_signature(from_keypair: &KeyPair, tx_sig: Signature, last_id: Hash) -> Self { let instruction = Instruction::ApplySignature(tx_sig); - Self::new_from_instruction(from_keypair, instruction, last_id) + Self::new_from_instruction(from_keypair, instruction, last_id, 0) } /// Create and sign a postdated Transaction. Used for unit-testing. @@ -110,20 +128,17 @@ impl Transaction { ); let plan = Plan::Budget(budget); let instruction = Instruction::NewContract(Contract { plan, tokens }); - let mut tx = Transaction { - instruction, - from, - last_id, - sig: Signature::default(), - }; - tx.sign(from_keypair); - tx + Self::new_from_instruction(from_keypair, instruction, last_id, 0) } fn get_sign_data(&self) -> Vec { let mut data = serialize(&(&self.instruction)).expect("serialize Contract"); 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); + data } @@ -139,7 +154,8 @@ impl Transaction { pub fn verify_plan(&self) -> bool { if let Instruction::NewContract(contract) = &self.instruction { - contract.plan.verify(contract.tokens) + self.fee >= 0 && self.fee <= contract.tokens + && contract.plan.verify(contract.tokens - self.fee) } else { true } @@ -189,6 +205,16 @@ mod tests { 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::new_taxed(&keypair0, pubkey1, 1, 1, zero).verify_plan()); + assert!(!Transaction::new_taxed(&keypair0, pubkey1, 1, 2, zero).verify_plan()); + assert!(!Transaction::new_taxed(&keypair0, pubkey1, 1, -1, zero).verify_plan()); + } + #[test] fn test_serialize_claim() { let budget = Budget::Pay(Payment { @@ -202,6 +228,7 @@ mod tests { from: Default::default(), last_id: Default::default(), sig: Default::default(), + fee: 0, }; let buf = serialize(&claim0).unwrap(); let claim1: Transaction = deserialize(&buf).unwrap(); diff --git a/tests/multinode.rs b/tests/multinode.rs index d602b8dd569b25..be4c469a7b5849 100644 --- a/tests/multinode.rs +++ b/tests/multinode.rs @@ -19,8 +19,8 @@ use std::io::sink; use std::net::UdpSocket; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; -use std::thread::JoinHandle; use std::thread::sleep; +use std::thread::JoinHandle; use std::time::Duration; fn validator(