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:
committed by
Marek Kotewicz
parent
03b96a7c0a
commit
1cd93e4ceb
216
ethcore/src/miner/pool_client.rs
Normal file
216
ethcore/src/miner/pool_client.rs
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Blockchain access for transaction pool.
|
||||
|
||||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use ethcore_miner::pool;
|
||||
use ethcore_miner::pool::client::NonceClient;
|
||||
use transaction::{
|
||||
self,
|
||||
UnverifiedTransaction,
|
||||
SignedTransaction,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use client::{TransactionId, BlockInfo, CallContract, Nonce};
|
||||
use engines::EthEngine;
|
||||
use header::Header;
|
||||
use miner;
|
||||
use miner::service_transaction_checker::ServiceTransactionChecker;
|
||||
|
||||
type NoncesCache = RwLock<HashMap<Address, U256>>;
|
||||
|
||||
const MAX_NONCE_CACHE_SIZE: usize = 4096;
|
||||
const EXPECTED_NONCE_CACHE_SIZE: usize = 2048;
|
||||
|
||||
/// Blockchain accesss for transaction pool.
|
||||
pub struct PoolClient<'a, C: 'a> {
|
||||
chain: &'a C,
|
||||
cached_nonces: CachedNonceClient<'a, C>,
|
||||
engine: &'a EthEngine,
|
||||
accounts: Option<&'a AccountProvider>,
|
||||
best_block_header: Header,
|
||||
service_transaction_checker: Option<ServiceTransactionChecker>,
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> Clone for PoolClient<'a, C> {
|
||||
fn clone(&self) -> Self {
|
||||
PoolClient {
|
||||
chain: self.chain,
|
||||
cached_nonces: self.cached_nonces.clone(),
|
||||
engine: self.engine,
|
||||
accounts: self.accounts.clone(),
|
||||
best_block_header: self.best_block_header.clone(),
|
||||
service_transaction_checker: self.service_transaction_checker.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> PoolClient<'a, C> where
|
||||
C: BlockInfo + CallContract,
|
||||
{
|
||||
/// Creates new client given chain, nonce cache, accounts and service transaction verifier.
|
||||
pub fn new(
|
||||
chain: &'a C,
|
||||
cache: &'a NoncesCache,
|
||||
engine: &'a EthEngine,
|
||||
accounts: Option<&'a AccountProvider>,
|
||||
refuse_service_transactions: bool,
|
||||
) -> Self {
|
||||
let best_block_header = chain.best_block_header();
|
||||
PoolClient {
|
||||
chain,
|
||||
cached_nonces: CachedNonceClient::new(chain, cache),
|
||||
engine,
|
||||
accounts,
|
||||
best_block_header,
|
||||
service_transaction_checker: if refuse_service_transactions {
|
||||
None
|
||||
} else {
|
||||
Some(Default::default())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies if signed transaction is executable.
|
||||
///
|
||||
/// This should perform any verifications that rely on chain status.
|
||||
pub fn verify_signed(&self, tx: &SignedTransaction) -> Result<(), transaction::Error> {
|
||||
self.engine.machine().verify_transaction(&tx, &self.best_block_header, self.chain)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> fmt::Debug for PoolClient<'a, C> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "PoolClient")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where
|
||||
C: miner::TransactionVerifierClient + Sync,
|
||||
{
|
||||
fn transaction_already_included(&self, hash: &H256) -> bool {
|
||||
self.chain.transaction_block(TransactionId::Hash(*hash)).is_some()
|
||||
}
|
||||
|
||||
fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result<SignedTransaction, transaction::Error> {
|
||||
self.engine.verify_transaction_basic(&tx, &self.best_block_header)?;
|
||||
let tx = self.engine.verify_transaction_unordered(tx, &self.best_block_header)?;
|
||||
|
||||
self.verify_signed(&tx)?;
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
fn account_details(&self, address: &Address) -> pool::client::AccountDetails {
|
||||
pool::client::AccountDetails {
|
||||
nonce: self.cached_nonces.account_nonce(address),
|
||||
balance: self.chain.latest_balance(address),
|
||||
is_local: self.accounts.map_or(false, |accounts| accounts.has_account(*address).unwrap_or(false)),
|
||||
}
|
||||
}
|
||||
|
||||
fn required_gas(&self, tx: &transaction::Transaction) -> U256 {
|
||||
tx.gas_required(&self.chain.latest_schedule()).into()
|
||||
}
|
||||
|
||||
fn transaction_type(&self, tx: &SignedTransaction) -> pool::client::TransactionType {
|
||||
match self.service_transaction_checker {
|
||||
None => pool::client::TransactionType::Regular,
|
||||
Some(ref checker) => match checker.check(self.chain, &tx) {
|
||||
Ok(true) => pool::client::TransactionType::Service,
|
||||
Ok(false) => pool::client::TransactionType::Regular,
|
||||
Err(e) => {
|
||||
debug!(target: "txqueue", "Unable to verify service transaction: {:?}", e);
|
||||
pool::client::TransactionType::Regular
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> NonceClient for PoolClient<'a, C> where
|
||||
C: Nonce + Sync,
|
||||
{
|
||||
fn account_nonce(&self, address: &Address) -> U256 {
|
||||
self.cached_nonces.account_nonce(address)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CachedNonceClient<'a, C: 'a> {
|
||||
client: &'a C,
|
||||
cache: &'a NoncesCache,
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> Clone for CachedNonceClient<'a, C> {
|
||||
fn clone(&self) -> Self {
|
||||
CachedNonceClient {
|
||||
client: self.client,
|
||||
cache: self.cache,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> fmt::Debug for CachedNonceClient<'a, C> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("CachedNonceClient")
|
||||
.field("cache", &self.cache.read().len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> CachedNonceClient<'a, C> {
|
||||
pub fn new(client: &'a C, cache: &'a NoncesCache) -> Self {
|
||||
CachedNonceClient {
|
||||
client,
|
||||
cache,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where
|
||||
C: Nonce + Sync,
|
||||
{
|
||||
fn account_nonce(&self, address: &Address) -> U256 {
|
||||
if let Some(nonce) = self.cache.read().get(address) {
|
||||
return *nonce;
|
||||
}
|
||||
|
||||
// We don't check again if cache has been populated.
|
||||
// It's not THAT expensive to fetch the nonce from state.
|
||||
let mut cache = self.cache.write();
|
||||
let nonce = self.client.latest_nonce(address);
|
||||
cache.insert(*address, nonce);
|
||||
|
||||
if cache.len() < MAX_NONCE_CACHE_SIZE {
|
||||
return nonce
|
||||
}
|
||||
|
||||
// Remove excessive amount of entries from the cache
|
||||
while cache.len() > EXPECTED_NONCE_CACHE_SIZE {
|
||||
// Just remove random entry
|
||||
if let Some(key) = cache.keys().next().cloned() {
|
||||
cache.remove(&key);
|
||||
}
|
||||
}
|
||||
nonce
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user