New Transaction Queue implementation (#8074)

* Implementation of Verifier, Scoring and Ready.

* Queue in progress.

* TransactionPool.

* Prepare for txpool release.

* Miner refactor [WiP]

* WiP reworking miner.

* Make it compile.

* Add some docs.

* Split blockchain access to a separate file.

* Work on miner API.

* Fix ethcore tests.

* Refactor miner interface for sealing/work packages.

* Implement next nonce.

* RPC compiles.

* Implement couple of missing methdods for RPC.

* Add transaction queue listeners.

* Compiles!

* Clean-up and parallelize.

* Get rid of RefCell in header.

* Revert "Get rid of RefCell in header."

This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb.

* Override Sync requirement.

* Fix status display.

* Unify logging.

* Extract some cheap checks.

* Measurements and optimizations.

* Fix scoring bug, heap size of bug and add cache

* Disable tx queueing and parallel verification.

* Make ethcore and ethcore-miner compile again.

* Make RPC compile again.

* Bunch of txpool tests.

* Migrate transaction queue tests.

* Nonce Cap

* Nonce cap cache and tests.

* Remove stale future transactions from the queue.

* Optimize scoring and write some tests.

* Simple penalization.

* Clean up and support for different scoring algorithms.

* Add CLI parameters for the new queue.

* Remove banning queue.

* Disable debug build.

* Change per_sender limit to be 1% instead of 5%

* Avoid cloning when propagating transactions.

* Remove old todo.

* Post-review fixes.

* Fix miner options default.

* Implement back ready transactions for light client.

* Get rid of from_pending_block

* Pass rejection reason.

* Add more details to drop.

* Rollback heap size of.

* Avoid cloning hashes when propagating and include more details on rejection.

* Fix tests.

* Introduce nonces cache.

* Remove uneccessary hashes allocation.

* Lower the mem limit.

* Re-enable parallel verification.

* Add miner log. Don't check the type if not below min_gas_price.

* Add more traces, fix disabling miner.

* Fix creating pending blocks twice on AuRa authorities.

* Fix tests.

* re-use pending blocks in AuRa

* Use reseal_min_period to prevent too frequent update_sealing.

* Fix log to contain hash not sender.

* Optimize local transactions.

* Fix aura tests.

* Update locks comments.

* Get rid of unsafe Sync impl.

* Review fixes.

* Remove excessive matches.

* Fix compilation errors.

* Use new pool in private transactions.

* Fix private-tx test.

* Fix secret store tests.

* Actually use gas_floor_target

* Fix config tests.

* Fix pool tests.

* Address grumbles.
This commit is contained in:
Tomasz Drwięga
2018-04-13 17:34:27 +02:00
committed by Marek Kotewicz
parent 03b96a7c0a
commit 1cd93e4ceb
105 changed files with 5185 additions and 5784 deletions

View File

@@ -21,11 +21,12 @@ use ethereum_types::U256;
use {scoring, Scoring, Ready, Readiness, Address as Sender};
use super::{Transaction, SharedTransaction};
#[derive(Default)]
#[derive(Debug, Default)]
pub struct DummyScoring;
impl Scoring<Transaction> for DummyScoring {
type Score = U256;
type Event = ();
fn compare(&self, old: &Transaction, new: &Transaction) -> cmp::Ordering {
old.nonce.cmp(&new.nonce)
@@ -43,9 +44,17 @@ impl Scoring<Transaction> for DummyScoring {
}
}
fn update_scores(&self, txs: &[SharedTransaction], scores: &mut [Self::Score], _change: scoring::Change) {
for i in 0..txs.len() {
scores[i] = txs[i].gas_price;
fn update_scores(&self, txs: &[SharedTransaction], scores: &mut [Self::Score], change: scoring::Change) {
if let scoring::Change::Event(_) = change {
// In case of event reset all scores to 0
for i in 0..txs.len() {
scores[i] = 0.into();
}
} else {
// Set to a gas price otherwise
for i in 0..txs.len() {
scores[i] = txs[i].gas_price;
}
}
}

View File

@@ -125,7 +125,7 @@ fn should_reject_if_above_count() {
let tx2 = b.tx().nonce(1).new();
let hash = *tx2.hash();
txq.import(tx1).unwrap();
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash));
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into()));
assert_eq!(txq.light_status().transaction_count, 1);
txq.clear();
@@ -151,7 +151,7 @@ fn should_reject_if_above_mem_usage() {
let tx2 = b.tx().nonce(2).mem_usage(2).new();
let hash = *tx2.hash();
txq.import(tx1).unwrap();
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash));
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into()));
assert_eq!(txq.light_status().transaction_count, 1);
txq.clear();
@@ -177,7 +177,7 @@ fn should_reject_if_above_sender_count() {
let tx2 = b.tx().nonce(2).new();
let hash = *tx2.hash();
txq.import(tx1).unwrap();
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash));
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into()));
assert_eq!(txq.light_status().transaction_count, 1);
txq.clear();
@@ -188,7 +188,7 @@ fn should_reject_if_above_sender_count() {
let hash = *tx2.hash();
txq.import(tx1).unwrap();
// This results in error because we also compare nonces
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash));
assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash, "0x0".into()));
assert_eq!(txq.light_status().transaction_count, 1);
}
@@ -249,6 +249,66 @@ fn should_construct_pending() {
assert_eq!(pending.next(), None);
}
#[test]
fn should_update_scoring_correctly() {
// given
let b = TransactionBuilder::default();
let mut txq = TestPool::default();
let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap();
let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap();
let tx2 = txq.import(b.tx().nonce(2).new()).unwrap();
// this transaction doesn't get to the block despite high gas price
// because of block gas limit and simplistic ordering algorithm.
txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap();
//gap
txq.import(b.tx().nonce(5).new()).unwrap();
let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap();
let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap();
let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap();
let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap();
// gap
txq.import(b.tx().sender(1).nonce(5).new()).unwrap();
let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap();
assert_eq!(txq.light_status().transaction_count, 11);
assert_eq!(txq.status(NonceReady::default()), Status {
stalled: 0,
pending: 9,
future: 2,
});
assert_eq!(txq.status(NonceReady::new(1)), Status {
stalled: 3,
pending: 6,
future: 2,
});
txq.update_scores(&0.into(), ());
// when
let mut current_gas = U256::zero();
let limit = (21_000 * 8).into();
let mut pending = txq.pending(NonceReady::default()).take_while(|tx| {
let should_take = tx.gas + current_gas <= limit;
if should_take {
current_gas = current_gas + tx.gas
}
should_take
});
assert_eq!(pending.next(), Some(tx9));
assert_eq!(pending.next(), Some(tx5));
assert_eq!(pending.next(), Some(tx6));
assert_eq!(pending.next(), Some(tx7));
assert_eq!(pending.next(), Some(tx8));
// penalized transactions
assert_eq!(pending.next(), Some(tx0));
assert_eq!(pending.next(), Some(tx1));
assert_eq!(pending.next(), Some(tx2));
assert_eq!(pending.next(), None);
}
#[test]
fn should_remove_transaction() {
// given
@@ -375,6 +435,20 @@ fn should_re_insert_after_cull() {
});
}
#[test]
fn should_return_worst_transaction() {
// given
let b = TransactionBuilder::default();
let mut txq = TestPool::default();
assert!(txq.worst_transaction().is_none());
// when
txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap();
// then
assert!(txq.worst_transaction().is_some());
}
mod listener {
use std::cell::RefCell;
use std::rc::Rc;
@@ -389,11 +463,11 @@ mod listener {
self.0.borrow_mut().push(if old.is_some() { "replaced" } else { "added" });
}
fn rejected(&mut self, _tx: Transaction) {
fn rejected(&mut self, _tx: &SharedTransaction, _reason: &error::ErrorKind) {
self.0.borrow_mut().push("rejected".into());
}
fn dropped(&mut self, _tx: &SharedTransaction) {
fn dropped(&mut self, _tx: &SharedTransaction, _new: Option<&Transaction>) {
self.0.borrow_mut().push("dropped".into());
}
@@ -401,8 +475,8 @@ mod listener {
self.0.borrow_mut().push("invalid".into());
}
fn cancelled(&mut self, _tx: &SharedTransaction) {
self.0.borrow_mut().push("cancelled".into());
fn canceled(&mut self, _tx: &SharedTransaction) {
self.0.borrow_mut().push("canceled".into());
}
fn mined(&mut self, _tx: &SharedTransaction) {
@@ -461,9 +535,9 @@ mod listener {
// then
txq.remove(&tx1.hash(), false);
assert_eq!(*results.borrow(), &["added", "added", "cancelled"]);
assert_eq!(*results.borrow(), &["added", "added", "canceled"]);
txq.remove(&tx2.hash(), true);
assert_eq!(*results.borrow(), &["added", "added", "cancelled", "invalid"]);
assert_eq!(*results.borrow(), &["added", "added", "canceled", "invalid"]);
assert_eq!(txq.light_status().transaction_count, 0);
}