Merge pull request #640 from ethcore/tx_queue_min_gas
Gas price threshold for transactions
This commit is contained in:
commit
cd37fa7cb5
@ -24,7 +24,7 @@ use ethcore::error::*;
|
|||||||
use ethcore::transaction::SignedTransaction;
|
use ethcore::transaction::SignedTransaction;
|
||||||
use transaction_queue::{TransactionQueue};
|
use transaction_queue::{TransactionQueue};
|
||||||
|
|
||||||
/// Miner external API
|
/// Miner client API
|
||||||
pub trait MinerService {
|
pub trait MinerService {
|
||||||
|
|
||||||
/// Returns miner's status.
|
/// Returns miner's status.
|
||||||
@ -34,12 +34,6 @@ pub trait MinerService {
|
|||||||
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T)
|
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T)
|
||||||
where T: Fn(&Address) -> U256;
|
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.
|
/// Removes all transactions from the queue and restart mining operation.
|
||||||
fn clear_and_reset(&self, chain: &BlockChainClient);
|
fn clear_and_reset(&self, chain: &BlockChainClient);
|
||||||
|
|
||||||
@ -103,6 +97,21 @@ impl Miner {
|
|||||||
fn extra_data(&self) -> Bytes {
|
fn extra_data(&self) -> Bytes {
|
||||||
self.extra_data.read().unwrap().clone()
|
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 {
|
impl MinerService for Miner {
|
||||||
@ -126,15 +135,6 @@ impl MinerService for Miner {
|
|||||||
transaction_queue.add_all(transactions, fetch_nonce);
|
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) {
|
fn prepare_sealing(&self, chain: &BlockChainClient) {
|
||||||
let no_of_transactions = 128;
|
let no_of_transactions = 128;
|
||||||
let transactions = self.transaction_queue.lock().unwrap().top_transactions(no_of_transactions);
|
let transactions = self.transaction_queue.lock().unwrap().top_transactions(no_of_transactions);
|
||||||
|
@ -159,6 +159,8 @@ pub struct TransactionQueueStatus {
|
|||||||
|
|
||||||
/// TransactionQueue implementation
|
/// TransactionQueue implementation
|
||||||
pub struct TransactionQueue {
|
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
|
/// Priority queue for transactions that can go to block
|
||||||
current: TransactionSet,
|
current: TransactionSet,
|
||||||
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
||||||
@ -189,6 +191,7 @@ impl TransactionQueue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TransactionQueue {
|
TransactionQueue {
|
||||||
|
minimal_gas_price: U256::zero(),
|
||||||
current: current,
|
current: current,
|
||||||
future: future,
|
future: future,
|
||||||
by_hash: HashMap::new(),
|
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
|
/// Returns current status for this queue
|
||||||
pub fn status(&self) -> TransactionQueueStatus {
|
pub fn status(&self) -> TransactionQueueStatus {
|
||||||
TransactionQueueStatus {
|
TransactionQueueStatus {
|
||||||
@ -215,6 +224,15 @@ impl TransactionQueue {
|
|||||||
/// Add signed transaction to queue to be verified and imported
|
/// Add signed transaction to queue to be verified and imported
|
||||||
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T)
|
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T)
|
||||||
where T: Fn(&Address) -> U256 {
|
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);
|
self.import_tx(VerifiedTransaction::new(tx), fetch_nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,6 +521,22 @@ mod test {
|
|||||||
assert_eq!(stats.pending, 1);
|
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]
|
#[test]
|
||||||
fn should_import_txs_from_same_sender() {
|
fn should_import_txs_from_same_sender() {
|
||||||
// given
|
// given
|
||||||
|
@ -110,6 +110,7 @@ API and Console Options:
|
|||||||
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible).
|
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible).
|
||||||
|
|
||||||
Sealing/Mining Options:
|
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
|
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
||||||
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
--extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
--extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
||||||
@ -166,6 +167,7 @@ struct Args {
|
|||||||
flag_rpcapi: Option<String>,
|
flag_rpcapi: Option<String>,
|
||||||
flag_logging: Option<String>,
|
flag_logging: Option<String>,
|
||||||
flag_version: bool,
|
flag_version: bool,
|
||||||
|
flag_gasprice: String,
|
||||||
flag_author: String,
|
flag_author: String,
|
||||||
flag_extra_data: Option<String>,
|
flag_extra_data: Option<String>,
|
||||||
}
|
}
|
||||||
@ -251,7 +253,15 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self) -> Address {
|
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 {
|
fn extra_data(&self) -> Bytes {
|
||||||
@ -274,7 +284,9 @@ impl Configuration {
|
|||||||
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||||
"morden" | "testnet" => ethereum::new_morden(),
|
"morden" | "testnet" => ethereum::new_morden(),
|
||||||
"olympic" => ethereum::new_olympic(),
|
"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 {
|
if self.args.flag_no_bootstrap { Vec::new() } else {
|
||||||
match self.args.arg_enode.len() {
|
match self.args.arg_enode.len() {
|
||||||
0 => spec.nodes().clone(),
|
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;
|
let mut public_address = None;
|
||||||
|
|
||||||
if let Some(ref a) = self.args.flag_address {
|
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;
|
listen_address = public_address;
|
||||||
}
|
}
|
||||||
if listen_address.is_none() {
|
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 let Some(ref a) = self.args.flag_public_address {
|
||||||
if public_address.is_some() {
|
if public_address.is_some() {
|
||||||
die!("Conflicting flags provided: --address and --public-address");
|
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)
|
(listen_address, public_address)
|
||||||
}
|
}
|
||||||
@ -426,6 +446,7 @@ impl Configuration {
|
|||||||
let miner = Miner::new();
|
let miner = Miner::new();
|
||||||
miner.set_author(self.author());
|
miner.set_author(self.author());
|
||||||
miner.set_extra_data(self.extra_data());
|
miner.set_extra_data(self.extra_data());
|
||||||
|
miner.set_minimal_gas_price(self.gasprice());
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
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_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr),
|
||||||
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
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);
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
// TODO: use this as the API list.
|
// TODO: use this as the API list.
|
||||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
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 report = client.report();
|
||||||
let sync_info = sync.status();
|
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 ]",
|
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
|
||||||
chain_info.best_block_number,
|
chain_info.best_block_number,
|
||||||
chain_info.best_block_hash,
|
chain_info.best_block_hash,
|
||||||
|
Loading…
Reference in New Issue
Block a user