Backports to master (#2530)
* Transaction queue limited by gas (#2528) * Use global state cache when mining (#2529)
This commit is contained in:
parent
72ec9366ad
commit
f200eb2cfe
@ -1095,7 +1095,7 @@ impl MiningBlockChainClient for Client {
|
|||||||
engine,
|
engine,
|
||||||
self.factories.clone(),
|
self.factories.clone(),
|
||||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||||
self.state_db.lock().boxed_clone(),
|
self.state_db.lock().boxed_clone_canon(&h),
|
||||||
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
||||||
self.build_last_hashes(h.clone()),
|
self.build_last_hashes(h.clone()),
|
||||||
author,
|
author,
|
||||||
|
@ -194,7 +194,7 @@ impl Miner {
|
|||||||
true => None,
|
true => None,
|
||||||
false => Some(WorkPoster::new(&options.new_work_notify))
|
false => Some(WorkPoster::new(&options.new_work_notify))
|
||||||
};
|
};
|
||||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)));
|
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, !U256::zero(), options.tx_gas_limit)));
|
||||||
Miner {
|
Miner {
|
||||||
transaction_queue: txq,
|
transaction_queue: txq,
|
||||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||||
@ -443,6 +443,8 @@ impl Miner {
|
|||||||
let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit();
|
let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit();
|
||||||
let mut queue = self.transaction_queue.lock();
|
let mut queue = self.transaction_queue.lock();
|
||||||
queue.set_gas_limit(gas_limit);
|
queue.set_gas_limit(gas_limit);
|
||||||
|
// Set total qx queue gas limit to be 2x the block gas limit.
|
||||||
|
queue.set_total_gas_limit(gas_limit << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if we had to prepare new pending block.
|
/// Returns true if we had to prepare new pending block.
|
||||||
|
@ -130,6 +130,8 @@ struct TransactionOrder {
|
|||||||
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
||||||
/// High nonce_height = Low priority (processed later)
|
/// High nonce_height = Low priority (processed later)
|
||||||
nonce_height: U256,
|
nonce_height: U256,
|
||||||
|
/// Gas specified in the transaction.
|
||||||
|
gas: U256,
|
||||||
/// Gas Price of the transaction.
|
/// Gas Price of the transaction.
|
||||||
/// Low gas price = Low priority (processed later)
|
/// Low gas price = Low priority (processed later)
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
@ -146,6 +148,7 @@ impl TransactionOrder {
|
|||||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
||||||
TransactionOrder {
|
TransactionOrder {
|
||||||
nonce_height: tx.nonce() - base_nonce,
|
nonce_height: tx.nonce() - base_nonce,
|
||||||
|
gas: tx.transaction.gas.clone(),
|
||||||
gas_price: tx.transaction.gas_price,
|
gas_price: tx.transaction.gas_price,
|
||||||
hash: tx.hash(),
|
hash: tx.hash(),
|
||||||
origin: tx.origin,
|
origin: tx.origin,
|
||||||
@ -287,6 +290,7 @@ struct TransactionSet {
|
|||||||
by_address: Table<Address, U256, TransactionOrder>,
|
by_address: Table<Address, U256, TransactionOrder>,
|
||||||
by_gas_price: GasPriceQueue,
|
by_gas_price: GasPriceQueue,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
|
gas_limit: U256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionSet {
|
impl TransactionSet {
|
||||||
@ -317,15 +321,18 @@ impl TransactionSet {
|
|||||||
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||||
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
||||||
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
|
||||||
let len = self.by_priority.len();
|
let mut count = 0;
|
||||||
if len <= self.limit {
|
let mut gas: U256 = 0.into();
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let to_drop : Vec<(Address, U256)> = {
|
let to_drop : Vec<(Address, U256)> = {
|
||||||
self.by_priority
|
self.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
.skip(self.limit)
|
.skip_while(|order| {
|
||||||
|
count = count + 1;
|
||||||
|
let r = gas.overflowing_add(order.gas);
|
||||||
|
if r.1 { return false }
|
||||||
|
gas = r.0;
|
||||||
|
count <= self.limit && gas <= self.gas_limit
|
||||||
|
})
|
||||||
.map(|order| by_hash.get(&order.hash)
|
.map(|order| by_hash.get(&order.hash)
|
||||||
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
||||||
.map(|tx| (tx.sender(), tx.nonce()))
|
.map(|tx| (tx.sender(), tx.nonce()))
|
||||||
@ -432,16 +439,17 @@ impl Default for TransactionQueue {
|
|||||||
impl TransactionQueue {
|
impl TransactionQueue {
|
||||||
/// Creates new instance of this Queue
|
/// Creates new instance of this Queue
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::with_limits(1024, !U256::zero())
|
Self::with_limits(1024, !U256::zero(), !U256::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new instance of this Queue with specified limits
|
/// Create new instance of this Queue with specified limits
|
||||||
pub fn with_limits(limit: usize, tx_gas_limit: U256) -> Self {
|
pub fn with_limits(limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self {
|
||||||
let current = TransactionSet {
|
let current = TransactionSet {
|
||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
gas_limit: gas_limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
let future = TransactionSet {
|
let future = TransactionSet {
|
||||||
@ -449,6 +457,7 @@ impl TransactionQueue {
|
|||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
gas_limit: gas_limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionQueue {
|
TransactionQueue {
|
||||||
@ -504,6 +513,13 @@ impl TransactionQueue {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets new total gas limit.
|
||||||
|
pub fn set_total_gas_limit(&mut self, gas_limit: U256) {
|
||||||
|
self.future.gas_limit = gas_limit;
|
||||||
|
self.current.gas_limit = gas_limit;
|
||||||
|
self.future.enforce_limit(&mut self.by_hash);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the new limit for the amount of gas any individual transaction may have.
|
/// Set the new limit for the amount of gas any individual transaction may have.
|
||||||
/// Any transaction already imported to the queue is not affected.
|
/// Any transaction already imported to the queue is not affected.
|
||||||
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
||||||
@ -827,6 +843,16 @@ impl TransactionQueue {
|
|||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Rough size sanity check
|
||||||
|
let gas = &tx.transaction.gas;
|
||||||
|
if U256::from(tx.transaction.data.len()) > *gas {
|
||||||
|
// Droping transaction
|
||||||
|
trace!(target: "txqueue", "Dropping oversized transaction: {:?} (gas: {} < size {})", hash, gas, tx.transaction.data.len());
|
||||||
|
return Err(TransactionError::LimitReached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The transaction might be old, let's check that.
|
// The transaction might be old, let's check that.
|
||||||
// This has to be the first test, otherwise calculating
|
// This has to be the first test, otherwise calculating
|
||||||
// nonce height would result in overflow.
|
// nonce height would result in overflow.
|
||||||
@ -979,6 +1005,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_nonce() -> U256 { 123.into() }
|
fn default_nonce() -> U256 { 123.into() }
|
||||||
|
fn default_gas_val() -> U256 { 100_000.into() }
|
||||||
fn default_gas_price() -> U256 { 1.into() }
|
fn default_gas_price() -> U256 { 1.into() }
|
||||||
|
|
||||||
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction {
|
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction {
|
||||||
@ -986,7 +1013,7 @@ mod test {
|
|||||||
action: Action::Create,
|
action: Action::Create,
|
||||||
value: U256::from(100),
|
value: U256::from(100),
|
||||||
data: "3331600055".from_hex().unwrap(),
|
data: "3331600055".from_hex().unwrap(),
|
||||||
gas: U256::from(100_000),
|
gas: default_gas_val(),
|
||||||
gas_price: gas_price,
|
gas_price: gas_price,
|
||||||
nonce: nonce
|
nonce: nonce
|
||||||
}
|
}
|
||||||
@ -1051,7 +1078,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero());
|
let mut txq = TransactionQueue::with_limits(2, !U256::zero(), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
||||||
let sender = tx1.sender().unwrap();
|
let sender = tx1.sender().unwrap();
|
||||||
let nonce = tx1.nonce;
|
let nonce = tx1.nonce;
|
||||||
@ -1089,7 +1116,8 @@ mod test {
|
|||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: 1
|
limit: 1,
|
||||||
|
gas_limit: !U256::zero(),
|
||||||
};
|
};
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
||||||
@ -1129,7 +1157,8 @@ mod test {
|
|||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: 1
|
limit: 1,
|
||||||
|
gas_limit: !U256::zero(),
|
||||||
};
|
};
|
||||||
// Create two transactions with same nonce
|
// Create two transactions with same nonce
|
||||||
// (same hash)
|
// (same hash)
|
||||||
@ -1177,7 +1206,8 @@ mod test {
|
|||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: 2
|
limit: 2,
|
||||||
|
gas_limit: !U256::zero(),
|
||||||
};
|
};
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||||
@ -1194,7 +1224,8 @@ mod test {
|
|||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
by_gas_price: Default::default(),
|
by_gas_price: Default::default(),
|
||||||
limit: 1
|
limit: 1,
|
||||||
|
gas_limit: !U256::zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
||||||
@ -1690,7 +1721,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let sender = tx.sender().unwrap();
|
let sender = tx.sender().unwrap();
|
||||||
let nonce = tx.nonce;
|
let nonce = tx.nonce;
|
||||||
@ -1711,7 +1742,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_limit_future_transactions() {
|
fn should_limit_future_transactions() {
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
||||||
txq.current.set_limit(10);
|
txq.current.set_limit(10);
|
||||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||||
@ -1728,6 +1759,16 @@ mod test {
|
|||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_limit_by_gas() {
|
||||||
|
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero());
|
||||||
|
let (tx1, _) = new_tx_pair_default(U256::from(4), U256::from(1));
|
||||||
|
let (tx3, _) = new_tx_pair_default(U256::from(4), U256::from(2));
|
||||||
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
|
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_drop_transactions_with_old_nonces() {
|
fn should_drop_transactions_with_old_nonces() {
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
@ -1971,7 +2012,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_keep_right_order_in_future() {
|
fn should_keep_right_order_in_future() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||||
default_account_details(a).balance };
|
default_account_details(a).balance };
|
||||||
|
@ -123,6 +123,7 @@ const MAX_ROUND_PARENTS: usize = 32;
|
|||||||
const MAX_NEW_HASHES: usize = 64;
|
const MAX_NEW_HASHES: usize = 64;
|
||||||
const MAX_TX_TO_IMPORT: usize = 512;
|
const MAX_TX_TO_IMPORT: usize = 512;
|
||||||
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
|
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
|
||||||
|
const MAX_TRANSACTION_SIZE: usize = 300*1024;
|
||||||
|
|
||||||
const STATUS_PACKET: u8 = 0x00;
|
const STATUS_PACKET: u8 = 0x00;
|
||||||
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
||||||
@ -1261,7 +1262,12 @@ impl ChainSync {
|
|||||||
item_count = min(item_count, MAX_TX_TO_IMPORT);
|
item_count = min(item_count, MAX_TX_TO_IMPORT);
|
||||||
let mut transactions = Vec::with_capacity(item_count);
|
let mut transactions = Vec::with_capacity(item_count);
|
||||||
for i in 0 .. item_count {
|
for i in 0 .. item_count {
|
||||||
let tx = try!(r.at(i)).as_raw().to_vec();
|
let rlp = try!(r.at(i));
|
||||||
|
if rlp.as_raw().len() > MAX_TRANSACTION_SIZE {
|
||||||
|
debug!("Skipped oversized transaction of {} bytes", rlp.as_raw().len());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let tx = rlp.as_raw().to_vec();
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
}
|
}
|
||||||
io.chain().queue_transactions(transactions);
|
io.chain().queue_transactions(transactions);
|
||||||
|
Loading…
Reference in New Issue
Block a user