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,
|
||||
self.factories.clone(),
|
||||
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"),
|
||||
self.build_last_hashes(h.clone()),
|
||||
author,
|
||||
|
@ -194,7 +194,7 @@ impl Miner {
|
||||
true => None,
|
||||
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 {
|
||||
transaction_queue: txq,
|
||||
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 mut queue = self.transaction_queue.lock();
|
||||
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.
|
||||
|
@ -130,6 +130,8 @@ struct TransactionOrder {
|
||||
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
||||
/// High nonce_height = Low priority (processed later)
|
||||
nonce_height: U256,
|
||||
/// Gas specified in the transaction.
|
||||
gas: U256,
|
||||
/// Gas Price of the transaction.
|
||||
/// Low gas price = Low priority (processed later)
|
||||
gas_price: U256,
|
||||
@ -146,6 +148,7 @@ impl TransactionOrder {
|
||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
||||
TransactionOrder {
|
||||
nonce_height: tx.nonce() - base_nonce,
|
||||
gas: tx.transaction.gas.clone(),
|
||||
gas_price: tx.transaction.gas_price,
|
||||
hash: tx.hash(),
|
||||
origin: tx.origin,
|
||||
@ -287,6 +290,7 @@ struct TransactionSet {
|
||||
by_address: Table<Address, U256, TransactionOrder>,
|
||||
by_gas_price: GasPriceQueue,
|
||||
limit: usize,
|
||||
gas_limit: U256,
|
||||
}
|
||||
|
||||
impl TransactionSet {
|
||||
@ -317,15 +321,18 @@ impl TransactionSet {
|
||||
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||
/// 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>> {
|
||||
let len = self.by_priority.len();
|
||||
if len <= self.limit {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
let mut gas: U256 = 0.into();
|
||||
let to_drop : Vec<(Address, U256)> = {
|
||||
self.by_priority
|
||||
.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)
|
||||
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
||||
.map(|tx| (tx.sender(), tx.nonce()))
|
||||
@ -432,16 +439,17 @@ impl Default for TransactionQueue {
|
||||
impl TransactionQueue {
|
||||
/// Creates new instance of this Queue
|
||||
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
|
||||
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 {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: limit,
|
||||
gas_limit: gas_limit,
|
||||
};
|
||||
|
||||
let future = TransactionSet {
|
||||
@ -449,6 +457,7 @@ impl TransactionQueue {
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: limit,
|
||||
gas_limit: gas_limit,
|
||||
};
|
||||
|
||||
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.
|
||||
/// Any transaction already imported to the queue is not affected.
|
||||
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
||||
@ -827,6 +843,16 @@ impl TransactionQueue {
|
||||
let nonce = tx.nonce();
|
||||
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.
|
||||
// This has to be the first test, otherwise calculating
|
||||
// nonce height would result in overflow.
|
||||
@ -979,6 +1005,7 @@ mod test {
|
||||
}
|
||||
|
||||
fn default_nonce() -> U256 { 123.into() }
|
||||
fn default_gas_val() -> U256 { 100_000.into() }
|
||||
fn default_gas_price() -> U256 { 1.into() }
|
||||
|
||||
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction {
|
||||
@ -986,7 +1013,7 @@ mod test {
|
||||
action: Action::Create,
|
||||
value: U256::from(100),
|
||||
data: "3331600055".from_hex().unwrap(),
|
||||
gas: U256::from(100_000),
|
||||
gas: default_gas_val(),
|
||||
gas_price: gas_price,
|
||||
nonce: nonce
|
||||
}
|
||||
@ -1051,7 +1078,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// 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 sender = tx1.sender().unwrap();
|
||||
let nonce = tx1.nonce;
|
||||
@ -1089,7 +1116,8 @@ mod test {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
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 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
||||
@ -1129,7 +1157,8 @@ mod test {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 1
|
||||
limit: 1,
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
// Create two transactions with same nonce
|
||||
// (same hash)
|
||||
@ -1177,7 +1206,8 @@ mod test {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 2
|
||||
limit: 2,
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
let tx = new_tx_default();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||
@ -1194,7 +1224,8 @@ mod test {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 1
|
||||
limit: 1,
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
|
||||
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
||||
@ -1690,7 +1721,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||
// 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 sender = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
@ -1711,7 +1742,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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);
|
||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||
@ -1728,6 +1759,16 @@ mod test {
|
||||
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]
|
||||
fn should_drop_transactions_with_old_nonces() {
|
||||
let mut txq = TransactionQueue::new();
|
||||
@ -1971,7 +2012,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_keep_right_order_in_future() {
|
||||
// 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 prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
default_account_details(a).balance };
|
||||
|
@ -123,6 +123,7 @@ const MAX_ROUND_PARENTS: usize = 32;
|
||||
const MAX_NEW_HASHES: usize = 64;
|
||||
const MAX_TX_TO_IMPORT: usize = 512;
|
||||
const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
|
||||
const MAX_TRANSACTION_SIZE: usize = 300*1024;
|
||||
|
||||
const STATUS_PACKET: u8 = 0x00;
|
||||
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
||||
@ -1261,7 +1262,12 @@ impl ChainSync {
|
||||
item_count = min(item_count, MAX_TX_TO_IMPORT);
|
||||
let mut transactions = Vec::with_capacity(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);
|
||||
}
|
||||
io.chain().queue_transactions(transactions);
|
||||
|
Loading…
Reference in New Issue
Block a user