diff --git a/miner/src/miner.rs b/miner/src/miner.rs index f5ad32d2d07..2c18f3a7931 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -24,7 +24,7 @@ use ethcore::error::*; use ethcore::transaction::SignedTransaction; use transaction_queue::{TransactionQueue}; -/// Miner external API +/// Miner client API pub trait MinerService { /// Returns miner's status. @@ -34,12 +34,6 @@ pub trait MinerService { fn import_transactions(&self, transactions: Vec, fetch_nonce: T) where T: Fn(&Address) -> U256; - /// Set the author that we will seal blocks as. - fn set_author(&self, author: Address); - - /// Set the extra_data that we will seal blocks with. - fn set_extra_data(&self, extra_data: Bytes); - /// Removes all transactions from the queue and restart mining operation. fn clear_and_reset(&self, chain: &BlockChainClient); @@ -103,6 +97,21 @@ impl Miner { fn extra_data(&self) -> Bytes { self.extra_data.read().unwrap().clone() } + + /// Set the author that we will seal blocks as. + pub fn set_author(&self, author: Address) { + *self.author.write().unwrap() = author; + } + + /// Set the extra_data that we will seal blocks with. + pub fn set_extra_data(&self, extra_data: Bytes) { + *self.extra_data.write().unwrap() = extra_data; + } + + /// Set minimal gas price of transaction to be accepted for mining. + pub fn set_minimal_gas_price(&self, min_gas_price: U256) { + self.transaction_queue.lock().unwrap().set_minimal_gas_price(min_gas_price); + } } impl MinerService for Miner { @@ -126,15 +135,6 @@ impl MinerService for Miner { transaction_queue.add_all(transactions, fetch_nonce); } - fn set_author(&self, author: Address) { - *self.author.write().unwrap() = author; - } - - - fn set_extra_data(&self, extra_data: Bytes) { - *self.extra_data.write().unwrap() = extra_data; - } - fn prepare_sealing(&self, chain: &BlockChainClient) { let no_of_transactions = 128; let transactions = self.transaction_queue.lock().unwrap().top_transactions(no_of_transactions); diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs index 3e0d931b550..ed8cf801e92 100644 --- a/miner/src/transaction_queue.rs +++ b/miner/src/transaction_queue.rs @@ -159,6 +159,8 @@ pub struct TransactionQueueStatus { /// TransactionQueue implementation pub struct TransactionQueue { + /// Gas Price threshold for transactions that can be imported to this queue (defaults to 0) + minimal_gas_price: U256, /// Priority queue for transactions that can go to block current: TransactionSet, /// Priority queue for transactions that has been received but are not yet valid to go to block @@ -189,6 +191,7 @@ impl TransactionQueue { }; TransactionQueue { + minimal_gas_price: U256::zero(), current: current, future: future, by_hash: HashMap::new(), @@ -196,6 +199,12 @@ impl TransactionQueue { } } + /// Sets new gas price threshold for incoming transactions. + /// Any transactions already imported to the queue are not affected. + pub fn set_minimal_gas_price(&mut self, min_gas_price: U256) { + self.minimal_gas_price = min_gas_price; + } + /// Returns current status for this queue pub fn status(&self) -> TransactionQueueStatus { TransactionQueueStatus { @@ -215,6 +224,15 @@ impl TransactionQueue { /// Add signed transaction to queue to be verified and imported pub fn add(&mut self, tx: SignedTransaction, fetch_nonce: &T) where T: Fn(&Address) -> U256 { + + if tx.gas_price < self.minimal_gas_price { + trace!(target: "sync", + "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", + tx.hash(), tx.gas_price, self.minimal_gas_price + ); + return; + } + // Everything ok - import transaction self.import_tx(VerifiedTransaction::new(tx), fetch_nonce); } @@ -503,6 +521,22 @@ mod test { assert_eq!(stats.pending, 1); } + #[test] + fn should_not_import_transaction_below_min_gas_price_threshold() { + // given + let mut txq = TransactionQueue::new(); + let tx = new_tx(); + txq.set_minimal_gas_price(tx.gas_price + U256::one()); + + // when + txq.add(tx, &default_nonce); + + // then + let stats = txq.status(); + assert_eq!(stats.pending, 0); + assert_eq!(stats.future, 0); + } + #[test] fn should_import_txs_from_same_sender() { // given diff --git a/parity/main.rs b/parity/main.rs index 6ded25c061a..b2d57fa2c11 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -110,6 +110,7 @@ API and Console Options: --rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible). Sealing/Mining Options: + --gasprice GAS Minimal gas price a transaction must have to be accepted for mining [default: 20000000000]. --author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63]. --extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters. @@ -166,6 +167,7 @@ struct Args { flag_rpcapi: Option, flag_logging: Option, flag_version: bool, + flag_gasprice: String, flag_author: String, flag_extra_data: Option, } @@ -251,7 +253,15 @@ impl Configuration { } fn author(&self) -> Address { - Address::from_str(&self.args.flag_author).unwrap_or_else(|_| die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author)) + Address::from_str(&self.args.flag_author).unwrap_or_else(|_| { + die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author) + }) + } + + fn gasprice(&self) -> U256 { + U256::from_dec_str(self.args.flag_gasprice).unwrap_or_else(|_| { + die("{}: Invalid gasprice given. Must be a decimal unsigned 256-bit number.") + }) } fn extra_data(&self) -> Bytes { @@ -274,7 +284,9 @@ impl Configuration { "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(), "morden" | "testnet" => ethereum::new_morden(), "olympic" => ethereum::new_olympic(), - f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()), + f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| { + die!("{}: Couldn't read chain specification file. Sure it exists?", f) + }).as_ref()), } } @@ -290,7 +302,9 @@ impl Configuration { if self.args.flag_no_bootstrap { Vec::new() } else { match self.args.arg_enode.len() { 0 => spec.nodes().clone(), - _ => self.args.arg_enode.iter().map(|s| Self::normalize_enode(s).unwrap_or_else(||die!("{}: Invalid node address format given for a boot node.", s))).collect(), + _ => self.args.arg_enode.iter().map(|s| Self::normalize_enode(s).unwrap_or_else(|| { + die!("{}: Invalid node address format given for a boot node.", s) + })).collect(), } } } @@ -301,17 +315,23 @@ impl Configuration { let mut public_address = None; if let Some(ref a) = self.args.flag_address { - public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --address", a))); + public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| { + die!("{}: Invalid listen/public address given with --address", a) + })); listen_address = public_address; } if listen_address.is_none() { - listen_address = Some(SocketAddr::from_str(self.args.flag_listen_address.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --listen-address", self.args.flag_listen_address))); + listen_address = Some(SocketAddr::from_str(self.args.flag_listen_address.as_ref()).unwrap_or_else(|_| { + die!("{}: Invalid listen/public address given with --listen-address", self.args.flag_listen_address) + })); } if let Some(ref a) = self.args.flag_public_address { if public_address.is_some() { die!("Conflicting flags provided: --address and --public-address"); } - public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| die!("{}: Invalid listen/public address given with --public-address", a))); + public_address = Some(SocketAddr::from_str(a.as_ref()).unwrap_or_else(|_| { + die!("{}: Invalid listen/public address given with --public-address", a) + })); } (listen_address, public_address) } @@ -426,6 +446,7 @@ impl Configuration { let miner = Miner::new(); miner.set_author(self.author()); miner.set_extra_data(self.extra_data()); + miner.set_minimal_gas_price(self.gasprice()); // Sync let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); @@ -436,7 +457,7 @@ impl Configuration { self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr), self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port) ); - SocketAddr::from_str(&url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen host/port given.", url)); + SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url)); let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors); // TODO: use this as the API list. let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis); @@ -514,7 +535,11 @@ impl Informant { let report = client.report(); let sync_info = sync.status(); - if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) { + if let (_, _, &Some(ref last_report)) = ( + self.chain_info.read().unwrap().deref(), + self.cache_info.read().unwrap().deref(), + self.report.read().unwrap().deref() + ) { println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]", chain_info.best_block_number, chain_info.best_block_hash,