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
File diff suppressed because it is too large
Load Diff
@@ -17,173 +17,87 @@
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Miner module
|
||||
//! Keeps track of transactions and mined block.
|
||||
//!
|
||||
//! Usage example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate ethcore;
|
||||
//! use std::env;
|
||||
//! use ethcore::ethereum;
|
||||
//! use ethcore::client::{Client, ClientConfig};
|
||||
//! use ethcore::miner::{Miner, MinerService};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let miner: Miner = Miner::with_spec(ðereum::new_foundation(&env::temp_dir()));
|
||||
//! // get status
|
||||
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
|
||||
//!
|
||||
//! // Check block for sealing
|
||||
//! //assert!(miner.sealing_block(&*client).lock().is_some());
|
||||
//! }
|
||||
//! ```
|
||||
//! Keeps track of transactions and currently sealed pending block.
|
||||
|
||||
mod miner;
|
||||
mod stratum;
|
||||
mod service_transaction_checker;
|
||||
|
||||
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
||||
pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOptions};
|
||||
pub mod pool_client;
|
||||
pub mod stratum;
|
||||
|
||||
pub use ethcore_miner::local_transactions::Status as LocalTransactionStatus;
|
||||
pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use block::{ClosedBlock, Block};
|
||||
use bytes::Bytes;
|
||||
use client::{
|
||||
MiningBlockChainClient, CallContract, RegistryInfo, ScheduleInfo,
|
||||
BlockChain, AccountData, BlockProducer, SealedBlockImporter
|
||||
};
|
||||
use error::{Error};
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions};
|
||||
|
||||
use block::{Block, SealedBlock};
|
||||
use client::{
|
||||
CallContract, RegistryInfo, ScheduleInfo,
|
||||
BlockChain, BlockProducer, SealedBlockImporter, ChainInfo,
|
||||
AccountData, Nonce,
|
||||
};
|
||||
use error::Error;
|
||||
use header::{BlockNumber, Header};
|
||||
use receipt::{RichReceipt, Receipt};
|
||||
use transaction::{UnverifiedTransaction, PendingTransaction, ImportResult as TransactionImportResult};
|
||||
use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction};
|
||||
use state::StateInfo;
|
||||
|
||||
/// Provides methods to verify incoming external transactions
|
||||
pub trait TransactionVerifierClient: Send + Sync
|
||||
// Required for ServiceTransactionChecker
|
||||
+ CallContract + RegistryInfo
|
||||
// Required for verifiying transactions
|
||||
+ BlockChain + ScheduleInfo + AccountData
|
||||
{}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
pub trait BlockChainClient: TransactionVerifierClient + BlockProducer + SealedBlockImporter {}
|
||||
|
||||
/// Miner client API
|
||||
pub trait MinerService : Send + Sync {
|
||||
/// Type representing chain state
|
||||
type State: StateInfo + 'static;
|
||||
|
||||
/// Returns miner's status.
|
||||
fn status(&self) -> MinerStatus;
|
||||
|
||||
/// Get the author that we will seal blocks as.
|
||||
fn author(&self) -> Address;
|
||||
|
||||
/// Set the author that we will seal blocks as.
|
||||
fn set_author(&self, author: Address);
|
||||
|
||||
/// Set info necessary to sign consensus messages.
|
||||
fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::SignError>;
|
||||
|
||||
/// Get the extra_data that we will seal blocks with.
|
||||
fn extra_data(&self) -> Bytes;
|
||||
|
||||
/// Set the extra_data that we will seal blocks with.
|
||||
fn set_extra_data(&self, extra_data: Bytes);
|
||||
|
||||
/// Get current minimal gas price for transactions accepted to queue.
|
||||
fn minimal_gas_price(&self) -> U256;
|
||||
|
||||
/// Set minimal gas price of transaction to be accepted for mining.
|
||||
fn set_minimal_gas_price(&self, min_gas_price: U256);
|
||||
|
||||
/// Get the lower bound of the gas limit we wish to target when sealing a new block.
|
||||
fn gas_floor_target(&self) -> U256;
|
||||
|
||||
/// Get the upper bound of the gas limit we wish to target when sealing a new block.
|
||||
fn gas_ceil_target(&self) -> U256;
|
||||
|
||||
// TODO: coalesce into single set_range function.
|
||||
/// Set the lower bound of gas limit we wish to target when sealing a new block.
|
||||
fn set_gas_floor_target(&self, target: U256);
|
||||
|
||||
/// Set the upper bound of gas limit we wish to target when sealing a new block.
|
||||
fn set_gas_ceil_target(&self, target: U256);
|
||||
|
||||
/// Get current transactions limit in queue.
|
||||
fn transactions_limit(&self) -> usize;
|
||||
|
||||
/// Set maximal number of transactions kept in the queue (both current and future).
|
||||
fn set_transactions_limit(&self, limit: usize);
|
||||
|
||||
/// Set maximum amount of gas allowed for any single transaction to mine.
|
||||
fn set_tx_gas_limit(&self, limit: U256);
|
||||
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_external_transactions<C: MiningBlockChainClient>(&self, client: &C, transactions: Vec<UnverifiedTransaction>) ->
|
||||
Vec<Result<TransactionImportResult, Error>>;
|
||||
|
||||
/// Imports own (node owner) transaction to queue.
|
||||
fn import_own_transaction<C: MiningBlockChainClient>(&self, chain: &C, transaction: PendingTransaction) ->
|
||||
Result<TransactionImportResult, Error>;
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256>;
|
||||
|
||||
/// Removes all transactions from the queue and restart mining operation.
|
||||
fn clear_and_reset<C: MiningBlockChainClient>(&self, chain: &C);
|
||||
|
||||
/// Called when blocks are imported to chain, updates transactions queue.
|
||||
fn chain_new_blocks<C>(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256])
|
||||
where C: AccountData + BlockChain + CallContract + RegistryInfo + BlockProducer + ScheduleInfo + SealedBlockImporter;
|
||||
|
||||
/// PoW chain - can produce work package
|
||||
fn can_produce_work_package(&self) -> bool;
|
||||
|
||||
/// New chain head event. Restart mining operation.
|
||||
fn update_sealing<C>(&self, chain: &C)
|
||||
where C: AccountData + BlockChain + RegistryInfo + CallContract + BlockProducer + SealedBlockImporter;
|
||||
// Sealing
|
||||
|
||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||
/// Will check the seal, but not actually insert the block into the chain.
|
||||
fn submit_seal<C: SealedBlockImporter>(&self, chain: &C, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
|
||||
|
||||
/// Get the sealing work package and if `Some`, apply some transform.
|
||||
fn map_sealing_work<C, F, T>(&self, client: &C, f: F) -> Option<T>
|
||||
where C: AccountData + BlockChain + BlockProducer + CallContract,
|
||||
F: FnOnce(&ClosedBlock) -> T,
|
||||
Self: Sized;
|
||||
|
||||
/// Query pending transactions for hash.
|
||||
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<PendingTransaction>;
|
||||
|
||||
/// Removes transaction from the queue.
|
||||
/// NOTE: The transaction is not removed from pending block if mining.
|
||||
fn remove_pending_transaction<C: AccountData>(&self, chain: &C, hash: &H256) -> Option<PendingTransaction>;
|
||||
|
||||
/// Get a list of all pending transactions in the queue.
|
||||
fn pending_transactions(&self) -> Vec<PendingTransaction>;
|
||||
|
||||
/// Get a list of all transactions that can go into the given block.
|
||||
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction>;
|
||||
|
||||
/// Get a list of all future transactions.
|
||||
fn future_transactions(&self) -> Vec<PendingTransaction>;
|
||||
|
||||
/// Get a list of local transactions with statuses.
|
||||
fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus>;
|
||||
|
||||
/// Get a list of all pending receipts.
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
|
||||
|
||||
/// Get a particular reciept.
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
|
||||
|
||||
/// Returns highest transaction nonce for given address.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<SealedBlock, Error>;
|
||||
|
||||
/// Is it currently sealing?
|
||||
fn is_currently_sealing(&self) -> bool;
|
||||
|
||||
/// Suggested gas price.
|
||||
fn sensible_gas_price(&self) -> U256;
|
||||
/// Get the sealing work package preparing it if doesn't exist yet.
|
||||
///
|
||||
/// Returns `None` if engine seals internally.
|
||||
fn work_package<C>(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)>
|
||||
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
|
||||
|
||||
/// Suggested gas limit.
|
||||
fn sensible_gas_limit(&self) -> U256 { 21000.into() }
|
||||
/// Update current pending block
|
||||
fn update_sealing<C>(&self, chain: &C)
|
||||
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
|
||||
|
||||
|
||||
// Notifications
|
||||
|
||||
/// Called when blocks are imported to chain, updates transactions queue.
|
||||
/// `is_internal_import` indicates that the block has just been created in miner and internally sealed by the engine,
|
||||
/// so we shouldn't attempt creating new block again.
|
||||
fn chain_new_blocks<C>(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool)
|
||||
where C: BlockChainClient;
|
||||
|
||||
|
||||
// Pending block
|
||||
|
||||
/// Get a list of all pending receipts from pending block.
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> Option<BTreeMap<H256, Receipt>>;
|
||||
|
||||
/// Get a particular receipt from pending block.
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||
fn pending_state(&self, latest_block_number: BlockNumber) -> Option<Self::State>;
|
||||
@@ -193,15 +107,79 @@ pub trait MinerService : Send + Sync {
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block or `None` if we're not sealing.
|
||||
fn pending_block(&self, latest_block_number: BlockNumber) -> Option<Block>;
|
||||
}
|
||||
|
||||
/// Mining status
|
||||
#[derive(Debug)]
|
||||
pub struct MinerStatus {
|
||||
/// Number of transactions in queue with state `pending` (ready to be included in block)
|
||||
pub transactions_in_pending_queue: usize,
|
||||
/// Number of transactions in queue with state `future` (not yet ready to be included in block)
|
||||
pub transactions_in_future_queue: usize,
|
||||
/// Number of transactions included in currently mined block
|
||||
pub transactions_in_pending_block: usize,
|
||||
/// Get `Some` `clone()` of the current pending block transactions or `None` if we're not sealing.
|
||||
fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option<Vec<SignedTransaction>>;
|
||||
|
||||
// Block authoring
|
||||
|
||||
/// Get current authoring parameters.
|
||||
fn authoring_params(&self) -> AuthoringParams;
|
||||
|
||||
/// Set the lower and upper bound of gas limit we wish to target when sealing a new block.
|
||||
fn set_gas_range_target(&self, gas_range_target: (U256, U256));
|
||||
|
||||
/// Set the extra_data that we will seal blocks with.
|
||||
fn set_extra_data(&self, extra_data: Bytes);
|
||||
|
||||
/// Set info necessary to sign consensus messages and block authoring.
|
||||
///
|
||||
/// On PoW password is optional.
|
||||
fn set_author(&self, address: Address, password: Option<String>) -> Result<(), ::account_provider::SignError>;
|
||||
|
||||
// Transaction Pool
|
||||
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_external_transactions<C>(&self, client: &C, transactions: Vec<UnverifiedTransaction>)
|
||||
-> Vec<Result<(), transaction::Error>>
|
||||
where C: BlockChainClient;
|
||||
|
||||
/// Imports own (node owner) transaction to queue.
|
||||
fn import_own_transaction<C>(&self, chain: &C, transaction: PendingTransaction)
|
||||
-> Result<(), transaction::Error>
|
||||
where C: BlockChainClient;
|
||||
|
||||
/// Removes transaction from the pool.
|
||||
///
|
||||
/// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers)
|
||||
/// there is a good chance that the transaction will actually be removed.
|
||||
/// NOTE: The transaction is not removed from pending block if there is one.
|
||||
fn remove_transaction(&self, hash: &H256) -> Option<Arc<VerifiedTransaction>>;
|
||||
|
||||
/// Query transaction from the pool given it's hash.
|
||||
fn transaction(&self, hash: &H256) -> Option<Arc<VerifiedTransaction>>;
|
||||
|
||||
/// Returns next valid nonce for given address.
|
||||
///
|
||||
/// This includes nonces of all transactions from this address in the pending queue
|
||||
/// if they are consecutive.
|
||||
/// NOTE: pool may contain some future transactions that will become pending after
|
||||
/// transaction with nonce returned from this function is signed on.
|
||||
fn next_nonce<C>(&self, chain: &C, address: &Address) -> U256
|
||||
where C: Nonce + Sync;
|
||||
|
||||
/// Get a list of all ready transactions.
|
||||
///
|
||||
/// Depending on the settings may look in transaction pool or only in pending block.
|
||||
fn ready_transactions<C>(&self, chain: &C) -> Vec<Arc<VerifiedTransaction>>
|
||||
where C: ChainInfo + Nonce + Sync;
|
||||
|
||||
/// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet).
|
||||
fn queued_transactions(&self) -> Vec<Arc<VerifiedTransaction>>;
|
||||
|
||||
/// Get a list of local transactions with statuses.
|
||||
fn local_transactions(&self) -> BTreeMap<H256, local_transactions::Status>;
|
||||
|
||||
/// Get current queue status.
|
||||
///
|
||||
/// Status includes verification thresholds and current pool utilization and limits.
|
||||
fn queue_status(&self) -> QueueStatus;
|
||||
|
||||
// Misc
|
||||
|
||||
/// Suggested gas price.
|
||||
fn sensible_gas_price(&self) -> U256;
|
||||
|
||||
/// Suggested gas limit.
|
||||
fn sensible_gas_limit(&self) -> U256;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -16,33 +16,38 @@
|
||||
|
||||
//! A service transactions contract checker.
|
||||
|
||||
use client::{RegistryInfo, CallContract};
|
||||
use client::{RegistryInfo, CallContract, BlockId};
|
||||
use transaction::SignedTransaction;
|
||||
use types::ids::BlockId;
|
||||
|
||||
use_contract!(service_transaction, "ServiceTransaction", "res/contracts/service_transaction.json");
|
||||
|
||||
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";
|
||||
|
||||
/// Service transactions checker.
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ServiceTransactionChecker {
|
||||
contract: service_transaction::ServiceTransaction,
|
||||
}
|
||||
|
||||
impl ServiceTransactionChecker {
|
||||
/// Checks if service transaction can be appended to the transaction queue.
|
||||
/// Checks if given address is whitelisted to send service transactions.
|
||||
pub fn check<C: CallContract + RegistryInfo>(&self, client: &C, tx: &SignedTransaction) -> Result<bool, String> {
|
||||
assert!(tx.gas_price.is_zero());
|
||||
let sender = tx.sender();
|
||||
let hash = tx.hash();
|
||||
|
||||
// Skip checking the contract if the transaction does not have zero gas price
|
||||
if !tx.gas_price.is_zero() {
|
||||
return Ok(false)
|
||||
}
|
||||
|
||||
let address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest)
|
||||
.ok_or_else(|| "contract is not configured")?;
|
||||
|
||||
trace!(target: "txqueue", "Checking service transaction checker contract from {}", address);
|
||||
trace!(target: "txqueue", "[{:?}] Checking service transaction checker contract from {}", hash, sender);
|
||||
|
||||
self.contract.functions()
|
||||
.certified()
|
||||
.call(tx.sender(), &|data| client.call_contract(BlockId::Latest, address, data))
|
||||
.call(sender, &|data| client.call_contract(BlockId::Latest, address, data))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ use std::sync::{Arc, Weak};
|
||||
use std::net::{SocketAddr, AddrParseError};
|
||||
use std::fmt;
|
||||
|
||||
use block::IsBlock;
|
||||
use client::Client;
|
||||
use client::{Client, ImportSealedBlock};
|
||||
use ethereum_types::{H64, H256, clean_0x, U256};
|
||||
use ethereum::ethash::Ethash;
|
||||
use ethash::SeedHashCompute;
|
||||
@@ -30,7 +29,7 @@ use ethcore_stratum::{
|
||||
JobDispatcher, PushWorkHandler,
|
||||
Stratum as StratumService, Error as StratumServiceError,
|
||||
};
|
||||
use miner::{self, Miner, MinerService};
|
||||
use miner::{Miner, MinerService};
|
||||
use parking_lot::Mutex;
|
||||
use rlp::encode;
|
||||
|
||||
@@ -120,14 +119,9 @@ impl JobDispatcher for StratumJobDispatcher {
|
||||
}
|
||||
|
||||
fn job(&self) -> Option<String> {
|
||||
self.with_core(|client, miner| miner.map_sealing_work(&*client, |b| {
|
||||
let pow_hash = b.hash();
|
||||
let number = b.block().header().number();
|
||||
let difficulty = b.block().header().difficulty();
|
||||
|
||||
self.payload(pow_hash, *difficulty, number)
|
||||
})
|
||||
)
|
||||
self.with_core(|client, miner| miner.work_package(&*client).map(|(pow_hash, number, _timestamp, difficulty)| {
|
||||
self.payload(pow_hash, difficulty, number)
|
||||
}))
|
||||
}
|
||||
|
||||
fn submit(&self, payload: Vec<String>) -> Result<(), StratumServiceError> {
|
||||
@@ -145,7 +139,10 @@ impl JobDispatcher for StratumJobDispatcher {
|
||||
|
||||
self.with_core_result(|client, miner| {
|
||||
let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()];
|
||||
match miner.submit_seal(&*client, payload.pow_hash, seal) {
|
||||
|
||||
let import = miner.submit_seal(payload.pow_hash, seal)
|
||||
.and_then(|block| client.import_sealed_block(block));
|
||||
match import {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "submit_seal error: {:?}", e);
|
||||
@@ -247,8 +244,8 @@ impl Stratum {
|
||||
|
||||
/// Start STRATUM job dispatcher and register it in the miner
|
||||
pub fn register(cfg: &Options, miner: Arc<Miner>, client: Weak<Client>) -> Result<(), Error> {
|
||||
let stratum = miner::Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?;
|
||||
miner.push_notifier(Box::new(stratum) as Box<NotifyWork>);
|
||||
let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?;
|
||||
miner.add_work_listener(Box::new(stratum) as Box<NotifyWork>);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user