2016-03-08 15:46:44 +01:00
// Copyright 2015, 2016 Ethcore (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/>.
use rayon ::prelude ::* ;
2016-06-29 16:26:19 +02:00
use std ::time ::{ Instant , Duration } ;
2016-03-11 14:48:30 +01:00
2016-03-27 15:39:45 +02:00
use util ::* ;
2016-07-06 11:23:29 +02:00
use util ::using_queue ::{ UsingQueue , GetAction } ;
2016-12-05 18:08:16 +01:00
use account_provider ::{ AccountProvider , Error as AccountError } ;
2016-05-31 19:52:53 +02:00
use views ::{ BlockView , HeaderView } ;
2016-11-18 17:45:19 +01:00
use header ::Header ;
2016-11-03 22:22:25 +01:00
use state ::{ State , CleanupMode } ;
2016-06-06 12:17:30 +02:00
use client ::{ MiningBlockChainClient , Executive , Executed , EnvInfo , TransactOptions , BlockID , CallAnalytics } ;
2016-11-16 17:54:54 +01:00
use client ::TransactionImportResult ;
2016-08-17 19:25:02 +02:00
use executive ::contract_address ;
2016-09-13 15:09:07 +02:00
use block ::{ ClosedBlock , SealedBlock , IsBlock , Block } ;
2016-05-31 19:52:53 +02:00
use error ::* ;
2016-08-17 19:25:02 +02:00
use transaction ::{ Action , SignedTransaction } ;
use receipt ::{ Receipt , RichReceipt } ;
2016-05-31 19:52:53 +02:00
use spec ::Spec ;
2016-07-28 20:32:20 +02:00
use engines ::Engine ;
2016-10-15 14:46:33 +02:00
use miner ::{ MinerService , MinerStatus , TransactionQueue , PrioritizationStrategy , AccountDetails , TransactionOrigin } ;
2016-10-27 19:28:34 +02:00
use miner ::banning_queue ::{ BanningTransactionQueue , Threshold } ;
2016-06-29 20:04:52 +02:00
use miner ::work_notify ::WorkPoster ;
2016-07-08 17:26:06 +02:00
use miner ::price_info ::PriceInfo ;
2016-11-16 17:54:54 +01:00
use miner ::local_transactions ::{ Status as LocalTransactionStatus } ;
2016-09-13 15:09:07 +02:00
use header ::BlockNumber ;
2016-07-01 21:13:56 +02:00
2016-06-27 19:06:54 +02:00
/// Different possible definitions for pending transaction set.
2016-07-25 16:09:47 +02:00
#[ derive(Debug, PartialEq) ]
2016-06-27 19:06:54 +02:00
pub enum PendingSet {
/// Always just the transactions in the queue. These have had only cheap checks.
AlwaysQueue ,
/// Always just the transactions in the sealing block. These have had full checks but
/// may be empty if the node is not actively mining or has force_sealing enabled.
AlwaysSealing ,
/// Try the sealing block, but if it is not currently sealing, fallback to the queue.
SealingOrElseQueue ,
}
2016-10-10 23:04:43 +02:00
/// Type of the gas limit to apply to the transaction queue.
#[ derive(Debug, PartialEq) ]
pub enum GasLimit {
/// Depends on the block gas limit and is updated with every block.
Auto ,
/// No limit.
None ,
/// Set to a fixed gas value.
Fixed ( U256 ) ,
}
2016-10-27 19:28:34 +02:00
/// Transaction queue banning settings.
#[ derive(Debug, PartialEq, Clone) ]
pub enum Banning {
/// Banning in transaction queue is disabled
Disabled ,
/// Banning in transaction queue is enabled
Enabled {
/// Upper limit of transaction processing time before banning.
offend_threshold : Duration ,
/// Number of similar offending transactions before banning.
min_offends : u16 ,
/// Number of seconds the offender is banned for.
ban_duration : Duration ,
} ,
}
2016-06-27 17:23:54 +02:00
/// Configures the behaviour of the miner.
2016-07-25 16:09:47 +02:00
#[ derive(Debug, PartialEq) ]
2016-06-27 17:23:54 +02:00
pub struct MinerOptions {
2016-06-29 15:37:11 +02:00
/// URLs to notify when there is new work.
pub new_work_notify : Vec < String > ,
2016-06-27 17:23:54 +02:00
/// Force the miner to reseal, even when nobody has asked for work.
pub force_sealing : bool ,
/// Reseal on receipt of new external transactions.
pub reseal_on_external_tx : bool ,
/// Reseal on receipt of new local transactions.
pub reseal_on_own_tx : bool ,
2016-06-29 16:26:19 +02:00
/// Minimum period between transaction-inspired reseals.
pub reseal_min_period : Duration ,
2016-06-28 10:21:29 +02:00
/// Maximum amount of gas to bother considering for block insertion.
2016-06-28 10:40:35 +02:00
pub tx_gas_limit : U256 ,
2016-06-27 20:19:01 +02:00
/// Maximum size of the transaction queue.
pub tx_queue_size : usize ,
2016-10-15 14:46:33 +02:00
/// Strategy to use for prioritizing transactions in the queue.
pub tx_queue_strategy : PrioritizationStrategy ,
2016-06-27 18:27:06 +02:00
/// Whether we should fallback to providing all the queue's transactions or just pending.
2016-06-27 19:06:54 +02:00
pub pending_set : PendingSet ,
2016-06-29 16:26:19 +02:00
/// How many historical work packages can we store before running out?
pub work_queue_size : usize ,
2016-06-30 12:56:58 +02:00
/// Can we submit two different solutions for the same block and expect both to result in an import?
pub enable_resubmission : bool ,
2016-10-10 23:04:43 +02:00
/// Global gas limit for all transaction in the queue except for local and retracted.
pub tx_queue_gas_limit : GasLimit ,
2016-10-27 19:28:34 +02:00
/// Banning settings
pub tx_queue_banning : Banning ,
2016-06-27 17:23:54 +02:00
}
impl Default for MinerOptions {
fn default ( ) -> Self {
MinerOptions {
2016-06-29 15:37:11 +02:00
new_work_notify : vec ! [ ] ,
2016-06-27 17:23:54 +02:00
force_sealing : false ,
2016-07-25 16:09:47 +02:00
reseal_on_external_tx : false ,
2016-06-27 17:23:54 +02:00
reseal_on_own_tx : true ,
2016-06-28 10:40:35 +02:00
tx_gas_limit : ! U256 ::zero ( ) ,
2016-10-15 14:46:33 +02:00
tx_queue_size : 1024 ,
tx_queue_gas_limit : GasLimit ::Auto ,
2016-10-27 19:27:08 +02:00
tx_queue_strategy : PrioritizationStrategy ::GasPriceOnly ,
2016-06-27 19:06:54 +02:00
pending_set : PendingSet ::AlwaysQueue ,
2016-07-25 16:09:47 +02:00
reseal_min_period : Duration ::from_secs ( 2 ) ,
2016-06-29 16:26:19 +02:00
work_queue_size : 20 ,
2016-06-30 12:56:58 +02:00
enable_resubmission : true ,
2016-10-27 19:28:34 +02:00
tx_queue_banning : Banning ::Disabled ,
2016-06-27 17:23:54 +02:00
}
}
}
2016-07-08 17:26:06 +02:00
/// Options for the dynamic gas price recalibrator.
2016-07-25 16:09:47 +02:00
#[ derive(Debug, PartialEq) ]
2016-07-08 17:26:06 +02:00
pub struct GasPriceCalibratorOptions {
/// Base transaction price to match against.
pub usd_per_tx : f32 ,
2016-07-10 13:18:33 +02:00
/// How frequently we should recalibrate.
2016-07-08 17:26:06 +02:00
pub recalibration_period : Duration ,
}
2016-07-10 13:18:33 +02:00
/// The gas price validator variant for a `GasPricer`.
2016-07-25 16:09:47 +02:00
#[ derive(Debug, PartialEq) ]
2016-07-08 17:26:06 +02:00
pub struct GasPriceCalibrator {
options : GasPriceCalibratorOptions ,
next_calibration : Instant ,
}
impl GasPriceCalibrator {
fn recalibrate < F : Fn ( U256 ) + Sync + Send + 'static > ( & mut self , set_price : F ) {
trace! ( target : " miner " , " Recalibrating {:?} versus {:?} " , Instant ::now ( ) , self . next_calibration ) ;
if Instant ::now ( ) > = self . next_calibration {
let usd_per_tx = self . options . usd_per_tx ;
trace! ( target : " miner " , " Getting price info " ) ;
2016-11-28 13:20:49 +01:00
let price_info = PriceInfo ::get ( move | price : PriceInfo | {
2016-07-08 17:26:06 +02:00
trace! ( target : " miner " , " Price info arrived: {:?} " , price ) ;
let usd_per_eth = price . ethusd ;
let wei_per_usd : f32 = 1.0e18 / usd_per_eth ;
let gas_per_tx : f32 = 21000.0 ;
let wei_per_gas : f32 = wei_per_usd * usd_per_tx / gas_per_tx ;
2016-07-15 10:11:14 +02:00
info! ( target : " miner " , " Updated conversion rate to Ξ1 = {} ({} wei/gas) " , Colour ::White . bold ( ) . paint ( format! ( " US$ {} " , usd_per_eth ) ) , Colour ::Yellow . bold ( ) . paint ( format! ( " {} " , wei_per_gas ) ) ) ;
2016-10-20 23:41:15 +02:00
set_price ( U256 ::from ( wei_per_gas as u64 ) ) ;
2016-11-28 13:20:49 +01:00
} ) ;
if price_info . is_ok ( ) {
2016-07-08 17:26:06 +02:00
self . next_calibration = Instant ::now ( ) + self . options . recalibration_period ;
} else {
warn! ( target : " miner " , " Unable to update Ether price. " ) ;
}
}
}
}
/// Struct to look after updating the acceptable gas price of a miner.
2016-07-25 16:09:47 +02:00
#[ derive(Debug, PartialEq) ]
2016-07-08 17:26:06 +02:00
pub enum GasPricer {
/// A fixed gas price in terms of Wei - always the argument given.
Fixed ( U256 ) ,
/// Gas price is calibrated according to a fixed amount of USD.
Calibrated ( GasPriceCalibrator ) ,
}
impl GasPricer {
/// Create a new Calibrated `GasPricer`.
pub fn new_calibrated ( options : GasPriceCalibratorOptions ) -> GasPricer {
GasPricer ::Calibrated ( GasPriceCalibrator {
options : options ,
next_calibration : Instant ::now ( ) ,
} )
}
/// Create a new Fixed `GasPricer`.
pub fn new_fixed ( gas_price : U256 ) -> GasPricer {
GasPricer ::Fixed ( gas_price )
}
fn recalibrate < F : Fn ( U256 ) + Sync + Send + 'static > ( & mut self , set_price : F ) {
match * self {
GasPricer ::Fixed ( ref max ) = > set_price ( max . clone ( ) ) ,
GasPricer ::Calibrated ( ref mut cal ) = > cal . recalibrate ( set_price ) ,
}
}
}
2016-07-28 21:06:36 +02:00
struct SealingWork {
queue : UsingQueue < ClosedBlock > ,
enabled : bool ,
}
2016-03-09 14:26:28 +01:00
/// Keeps track of transactions using priority queue and holds currently mined block.
2016-09-13 15:09:07 +02:00
/// Handles preparing work for "work sealing" or seals "internally" if Engine does not require work.
2016-03-08 15:46:44 +01:00
pub struct Miner {
2016-06-19 12:33:50 +02:00
// NOTE [ToDr] When locking always lock in this order!
2016-10-27 19:28:34 +02:00
transaction_queue : Arc < Mutex < BanningTransactionQueue > > ,
2016-07-28 21:06:36 +02:00
sealing_work : Mutex < SealingWork > ,
2016-08-09 11:45:07 +02:00
next_allowed_reseal : Mutex < Instant > ,
sealing_block_last_request : Mutex < u64 > ,
2016-03-08 15:46:44 +01:00
// for sealing...
2016-06-27 17:23:54 +02:00
options : MinerOptions ,
2016-11-04 16:50:56 +01:00
/// Does the node perform internal (without work) sealing.
pub seals_internally : bool ,
2016-08-04 18:17:21 +02:00
2016-06-23 14:29:16 +02:00
gas_range_target : RwLock < ( U256 , U256 ) > ,
2016-03-08 15:46:44 +01:00
author : RwLock < Address > ,
extra_data : RwLock < Bytes > ,
2016-08-05 23:33:55 +02:00
engine : Arc < Engine > ,
2016-05-03 17:23:53 +02:00
2016-05-30 13:10:33 +02:00
accounts : Option < Arc < AccountProvider > > ,
2016-06-29 20:04:52 +02:00
work_poster : Option < WorkPoster > ,
2016-07-08 17:26:06 +02:00
gas_pricer : Mutex < GasPricer > ,
2016-03-08 15:46:44 +01:00
}
2016-06-20 10:28:38 +02:00
impl Miner {
2016-09-15 12:12:15 +02:00
/// Creates new instance of miner.
fn new_raw ( options : MinerOptions , gas_pricer : GasPricer , spec : & Spec , accounts : Option < Arc < AccountProvider > > ) -> Miner {
let work_poster = match options . new_work_notify . is_empty ( ) {
true = > None ,
false = > Some ( WorkPoster ::new ( & options . new_work_notify ) )
} ;
2016-10-10 23:04:43 +02:00
let gas_limit = match options . tx_queue_gas_limit {
GasLimit ::Fixed ( ref limit ) = > * limit ,
_ = > ! U256 ::zero ( ) ,
} ;
2016-10-27 19:28:34 +02:00
let txq = TransactionQueue ::with_limits ( options . tx_queue_strategy , options . tx_queue_size , gas_limit , options . tx_gas_limit ) ;
let txq = match options . tx_queue_banning {
Banning ::Disabled = > BanningTransactionQueue ::new ( txq , Threshold ::NeverBan , Duration ::from_secs ( 180 ) ) ,
Banning ::Enabled { ban_duration , min_offends , .. } = > BanningTransactionQueue ::new (
txq ,
Threshold ::BanAfter ( min_offends ) ,
ban_duration ,
) ,
} ;
2016-09-15 12:12:15 +02:00
Miner {
2016-10-27 19:28:34 +02:00
transaction_queue : Arc ::new ( Mutex ::new ( txq ) ) ,
2016-06-29 16:26:19 +02:00
next_allowed_reseal : Mutex ::new ( Instant ::now ( ) ) ,
2016-05-03 17:23:53 +02:00
sealing_block_last_request : Mutex ::new ( 0 ) ,
2016-09-15 12:12:15 +02:00
sealing_work : Mutex ::new ( SealingWork {
queue : UsingQueue ::new ( options . work_queue_size ) ,
enabled : options . force_sealing
| | ! options . new_work_notify . is_empty ( )
| | spec . engine . is_default_sealer ( ) . unwrap_or ( false )
} ) ,
seals_internally : spec . engine . is_default_sealer ( ) . is_some ( ) ,
2016-06-23 14:29:16 +02:00
gas_range_target : RwLock ::new ( ( U256 ::zero ( ) , U256 ::zero ( ) ) ) ,
2016-05-03 17:23:53 +02:00
author : RwLock ::new ( Address ::default ( ) ) ,
extra_data : RwLock ::new ( Vec ::new ( ) ) ,
2016-06-29 16:26:19 +02:00
options : options ,
2016-06-20 10:28:38 +02:00
accounts : accounts ,
2016-08-05 23:33:55 +02:00
engine : spec . engine . clone ( ) ,
2016-06-29 20:04:52 +02:00
work_poster : work_poster ,
2016-07-08 17:26:06 +02:00
gas_pricer : Mutex ::new ( gas_pricer ) ,
2016-09-15 12:12:15 +02:00
}
}
2016-09-14 17:29:35 +02:00
/// Creates new instance of miner with accounts and with given spec.
pub fn with_spec_and_accounts ( spec : & Spec , accounts : Option < Arc < AccountProvider > > ) -> Miner {
Miner ::new_raw ( Default ::default ( ) , GasPricer ::new_fixed ( 20_000_000_000 u64 . into ( ) ) , spec , accounts )
}
2016-09-15 12:12:15 +02:00
/// Creates new instance of miner without accounts, but with given spec.
pub fn with_spec ( spec : & Spec ) -> Miner {
Miner ::new_raw ( Default ::default ( ) , GasPricer ::new_fixed ( 20_000_000_000 u64 . into ( ) ) , spec , None )
}
/// Creates new instance of a miner Arc.
pub fn new ( options : MinerOptions , gas_pricer : GasPricer , spec : & Spec , accounts : Option < Arc < AccountProvider > > ) -> Arc < Miner > {
Arc ::new ( Miner ::new_raw ( options , gas_pricer , spec , accounts ) )
2016-03-09 14:26:28 +01:00
}
2016-03-08 15:46:44 +01:00
2016-06-29 20:04:52 +02:00
fn forced_sealing ( & self ) -> bool {
self . options . force_sealing | | ! self . options . new_work_notify . is_empty ( )
}
2016-09-06 15:31:13 +02:00
/// Clear all pending block states
pub fn clear ( & self ) {
self . sealing_work . lock ( ) . queue . reset ( ) ;
}
2016-07-14 15:24:12 +02:00
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
pub fn pending_state ( & self ) -> Option < State > {
2016-07-28 21:06:36 +02:00
self . sealing_work . lock ( ) . queue . peek_last_ref ( ) . map ( | b | b . block ( ) . fields ( ) . state . clone ( ) )
2016-07-14 15:24:12 +02:00
}
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
pub fn pending_block ( & self ) -> Option < Block > {
2016-07-28 21:06:36 +02:00
self . sealing_work . lock ( ) . queue . peek_last_ref ( ) . map ( | b | b . base ( ) . clone ( ) )
2016-07-14 15:24:12 +02:00
}
2016-09-16 23:03:26 +02:00
#[ cfg_attr(feature= " dev " , allow(match_same_arms)) ]
2016-03-17 12:47:31 +01:00
/// Prepares new block for sealing including top transactions from queue.
2016-09-13 15:09:07 +02:00
fn prepare_block ( & self , chain : & MiningBlockChainClient ) -> ( ClosedBlock , Option < H256 > ) {
2016-07-08 17:26:06 +02:00
{
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_block: recalibrating... " ) ;
2016-07-08 17:26:06 +02:00
let txq = self . transaction_queue . clone ( ) ;
2016-07-13 19:59:59 +02:00
self . gas_pricer . lock ( ) . recalibrate ( move | price | {
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_block: Got gas price! {} " , price ) ;
2016-07-13 19:59:59 +02:00
txq . lock ( ) . set_minimal_gas_price ( price ) ;
2016-07-08 17:26:06 +02:00
} ) ;
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_block: done recalibration. " ) ;
2016-07-08 17:26:06 +02:00
}
2016-10-20 14:49:12 +02:00
let _timer = PerfTimer ::new ( " prepare_block " ) ;
2016-06-30 22:35:59 +02:00
let ( transactions , mut open_block , original_work_hash ) = {
2016-07-13 19:59:59 +02:00
let transactions = { self . transaction_queue . lock ( ) . top_transactions ( ) } ;
let mut sealing_work = self . sealing_work . lock ( ) ;
2016-07-28 21:06:36 +02:00
let last_work_hash = sealing_work . queue . peek_last_ref ( ) . map ( | pb | pb . block ( ) . fields ( ) . header . hash ( ) ) ;
2016-06-21 11:26:43 +02:00
let best_hash = chain . best_block_header ( ) . sha3 ( ) ;
2016-03-23 17:28:02 +01:00
/*
2016-06-21 11:26:43 +02:00
// check to see if last ClosedBlock in would_seals is actually same parent block.
// if so
// duplicate, re-open and push any new transactions.
// if at least one was pushed successfully, close and enqueue new ClosedBlock;
// otherwise, leave everything alone.
// otherwise, author a fresh block.
2016-03-23 17:28:02 +01:00
* /
2016-07-28 21:06:36 +02:00
let open_block = match sealing_work . queue . pop_if ( | b | b . block ( ) . fields ( ) . header . parent_hash ( ) = = & best_hash ) {
2016-06-21 11:26:43 +02:00
Some ( old_block ) = > {
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_block: Already have previous work; updating and returning " ) ;
2016-06-21 11:26:43 +02:00
// add transactions to old_block
2016-08-24 16:53:36 +02:00
old_block . reopen ( & * self . engine )
2016-06-21 11:26:43 +02:00
}
None = > {
// block not found - create it.
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_block: No existing work - making new block " ) ;
2016-06-21 11:26:43 +02:00
chain . prepare_open_block (
self . author ( ) ,
2016-06-23 14:29:16 +02:00
( self . gas_floor_target ( ) , self . gas_ceil_target ( ) ) ,
2016-06-21 11:26:43 +02:00
self . extra_data ( )
)
}
} ;
2016-06-30 13:12:15 +02:00
( transactions , open_block , last_work_hash )
2016-03-24 23:03:22 +01:00
} ;
2016-06-06 14:33:12 +02:00
let mut invalid_transactions = HashSet ::new ( ) ;
2016-09-23 17:26:36 +02:00
let mut transactions_to_penalize = HashSet ::new ( ) ;
2016-06-06 14:33:12 +02:00
let block_number = open_block . block ( ) . fields ( ) . header . number ( ) ;
2016-10-27 19:28:34 +02:00
// TODO Push new uncles too.
2016-06-06 14:33:12 +02:00
for tx in transactions {
let hash = tx . hash ( ) ;
2016-10-27 19:28:34 +02:00
let start = Instant ::now ( ) ;
let result = open_block . push_transaction ( tx , None ) ;
let took = start . elapsed ( ) ;
// Check for heavy transactions
match self . options . tx_queue_banning {
Banning ::Enabled { ref offend_threshold , .. } if & took > offend_threshold = > {
match self . transaction_queue . lock ( ) . ban_transaction ( & hash ) {
true = > {
warn! ( target : " miner " , " Detected heavy transaction. Banning the sender and recipient/code. " ) ;
} ,
false = > {
transactions_to_penalize . insert ( hash ) ;
debug! ( target : " miner " , " Detected heavy transaction. Penalizing sender. " )
}
}
} ,
_ = > { } ,
}
match result {
2016-07-18 13:50:45 +02:00
Err ( Error ::Execution ( ExecutionError ::BlockGasLimitReached { gas_limit , gas_used , gas } ) ) = > {
debug! ( target : " miner " , " Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?}) " , hash , gas_limit , gas_used , gas ) ;
2016-09-23 17:26:36 +02:00
// Penalize transaction if it's above current gas limit
if gas > gas_limit {
transactions_to_penalize . insert ( hash ) ;
}
2016-09-23 16:24:11 +02:00
2016-06-06 14:33:12 +02:00
// Exit early if gas left is smaller then min_tx_gas
let min_tx_gas : U256 = 21000. into ( ) ; // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas {
break ;
}
} ,
2016-07-05 17:51:41 +02:00
// Invalid nonce error can happen only if previous transaction is skipped because of gas limit.
// If there is errornous state of transaction queue it will be fixed when next block is imported.
2016-07-18 13:50:45 +02:00
Err ( Error ::Execution ( ExecutionError ::InvalidNonce { expected , got } ) ) = > {
debug! ( target : " miner " , " Skipping adding transaction to block because of invalid nonce: {:?} (expected: {:?}, got: {:?}) " , hash , expected , got ) ;
2016-07-05 17:51:41 +02:00
} ,
// already have transaction - ignore
Err ( Error ::Transaction ( TransactionError ::AlreadyImported ) ) = > { } ,
2016-06-06 14:33:12 +02:00
Err ( e ) = > {
invalid_transactions . insert ( hash ) ;
2016-07-05 17:51:41 +02:00
debug! ( target : " miner " ,
2016-06-06 14:33:12 +02:00
" Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?} " ,
block_number , hash , e ) ;
} ,
_ = > { } // imported ok
}
}
let block = open_block . close ( ) ;
2016-04-15 07:38:23 +02:00
let fetch_account = | a : & Address | AccountDetails {
2016-05-26 22:17:55 +02:00
nonce : chain . latest_nonce ( a ) ,
balance : chain . latest_balance ( a ) ,
2016-04-15 07:38:23 +02:00
} ;
2016-06-06 14:33:12 +02:00
2016-06-21 11:26:43 +02:00
{
2016-07-13 19:59:59 +02:00
let mut queue = self . transaction_queue . lock ( ) ;
2016-10-27 08:28:12 +02:00
for hash in invalid_transactions {
2016-06-21 11:26:43 +02:00
queue . remove_invalid ( & hash , & fetch_account ) ;
}
2016-09-23 17:26:36 +02:00
for hash in transactions_to_penalize {
2016-09-23 16:24:11 +02:00
queue . penalize ( & hash ) ;
}
2016-04-15 07:38:23 +02:00
}
2016-09-13 15:09:07 +02:00
( block , original_work_hash )
}
/// Check is reseal is allowed and necessary.
fn requires_reseal ( & self , best_block : BlockNumber ) -> bool {
let has_local_transactions = self . transaction_queue . lock ( ) . has_local_pending_transactions ( ) ;
let mut sealing_work = self . sealing_work . lock ( ) ;
if sealing_work . enabled {
trace! ( target : " miner " , " requires_reseal: sealing enabled " ) ;
let last_request = * self . sealing_block_last_request . lock ( ) ;
let should_disable_sealing = ! self . forced_sealing ( )
& & ! has_local_transactions
2016-11-14 14:35:16 +01:00
& & ! self . seals_internally
2016-09-13 15:09:07 +02:00
& & best_block > last_request
& & best_block - last_request > SEALING_TIMEOUT_IN_BLOCKS ;
trace! ( target : " miner " , " requires_reseal: should_disable_sealing={}; best_block={}, last_request={} " , should_disable_sealing , best_block , last_request ) ;
if should_disable_sealing {
trace! ( target : " miner " , " Miner sleeping (current {}, last {}) " , best_block , last_request ) ;
sealing_work . enabled = false ;
sealing_work . queue . reset ( ) ;
false
} else {
// sealing enabled and we don't want to sleep.
* self . next_allowed_reseal . lock ( ) = Instant ::now ( ) + self . options . reseal_min_period ;
true
}
} else {
2016-09-15 12:12:15 +02:00
trace! ( target : " miner " , " requires_reseal: sealing is disabled " ) ;
2016-09-13 15:09:07 +02:00
false
}
}
/// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed),
/// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine.
2016-09-16 23:03:26 +02:00
fn seal_block_internally ( & self , block : ClosedBlock ) -> Result < SealedBlock , Option < ClosedBlock > > {
2016-12-05 18:08:16 +01:00
trace! ( target : " miner " , " seal_block_internally: attempting internal seal. " ) ;
let s = self . engine . generate_seal ( block . block ( ) ) ;
2016-09-13 15:09:07 +02:00
if let Some ( seal ) = s {
trace! ( target : " miner " , " seal_block_internally: managed internal seal. importing... " ) ;
2016-12-05 18:08:16 +01:00
block . lock ( ) . try_seal ( & * self . engine , seal ) . or_else ( | ( e , _ ) | {
warn! ( " prepare_sealing: ERROR: try_seal failed when given internally generated seal: {} " , e ) ;
2016-09-13 15:09:07 +02:00
Err ( None )
} )
} else {
trace! ( target : " miner " , " seal_block_internally: unable to generate seal internally " ) ;
Err ( Some ( block ) )
}
}
2016-06-06 14:33:12 +02:00
2016-09-13 15:09:07 +02:00
/// Uses Engine to seal the block internally and then imports it to chain.
fn seal_and_import_block_internally ( & self , chain : & MiningBlockChainClient , block : ClosedBlock ) -> bool {
2016-11-14 14:35:16 +01:00
if ! block . transactions ( ) . is_empty ( ) | | self . forced_sealing ( ) {
2016-09-13 15:09:07 +02:00
if let Ok ( sealed ) = self . seal_block_internally ( block ) {
2016-12-05 18:08:16 +01:00
if chain . import_sealed_block ( sealed ) . is_ok ( ) {
2016-09-19 10:38:47 +02:00
trace! ( target : " miner " , " import_block_internally: imported internally sealed block " ) ;
2016-09-13 15:09:07 +02:00
return true
2016-05-03 17:23:53 +02:00
}
2016-03-26 20:36:03 +01:00
}
2016-03-22 13:05:18 +01:00
}
2016-09-13 15:09:07 +02:00
false
}
2016-06-21 11:26:43 +02:00
2016-09-13 15:09:07 +02:00
/// Prepares work which has to be done to seal.
fn prepare_work ( & self , block : ClosedBlock , original_work_hash : Option < H256 > ) {
2016-06-30 22:35:59 +02:00
let ( work , is_new ) = {
2016-07-13 19:59:59 +02:00
let mut sealing_work = self . sealing_work . lock ( ) ;
2016-07-28 21:06:36 +02:00
let last_work_hash = sealing_work . queue . peek_last_ref ( ) . map ( | pb | pb . block ( ) . fields ( ) . header . hash ( ) ) ;
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_work: Checking whether we need to reseal: orig={:?} last={:?}, this={:?} " , original_work_hash , last_work_hash , block . block ( ) . fields ( ) . header . hash ( ) ) ;
2016-06-30 22:35:59 +02:00
let ( work , is_new ) = if last_work_hash . map_or ( true , | h | h ! = block . block ( ) . fields ( ) . header . hash ( ) ) {
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_work: Pushing a new, refreshed or borrowed pending {}... " , block . block ( ) . fields ( ) . header . hash ( ) ) ;
2016-06-29 20:04:52 +02:00
let pow_hash = block . block ( ) . fields ( ) . header . hash ( ) ;
let number = block . block ( ) . fields ( ) . header . number ( ) ;
let difficulty = * block . block ( ) . fields ( ) . header . difficulty ( ) ;
2016-06-30 23:14:54 +02:00
let is_new = original_work_hash . map_or ( true , | h | block . block ( ) . fields ( ) . header . hash ( ) ! = h ) ;
2016-07-28 21:06:36 +02:00
sealing_work . queue . push ( block ) ;
2016-07-01 20:38:37 +02:00
// If push notifications are enabled we assume all work items are used.
if self . work_poster . is_some ( ) & & is_new {
2016-07-28 21:06:36 +02:00
sealing_work . queue . use_last_ref ( ) ;
2016-07-01 20:38:37 +02:00
}
2016-06-30 22:35:59 +02:00
( Some ( ( pow_hash , difficulty , number ) ) , is_new )
2016-06-29 20:04:52 +02:00
} else {
2016-06-30 22:35:59 +02:00
( None , false )
2016-06-29 20:04:52 +02:00
} ;
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_work: leaving (last={:?}) " , sealing_work . queue . peek_last_ref ( ) . map ( | b | b . block ( ) . fields ( ) . header . hash ( ) ) ) ;
2016-06-30 22:35:59 +02:00
( work , is_new )
2016-06-29 20:04:52 +02:00
} ;
2016-06-30 22:35:59 +02:00
if is_new {
2016-08-17 16:06:41 +02:00
work . map ( | ( pow_hash , difficulty , number ) | self . work_poster . as_ref ( ) . map ( | p | p . notify ( pow_hash , difficulty , number ) ) ) ;
2016-06-30 22:35:59 +02:00
}
2016-03-18 19:36:32 +01:00
}
2016-05-31 20:33:26 +02:00
fn update_gas_limit ( & self , chain : & MiningBlockChainClient ) {
2016-03-17 15:20:33 +01:00
let gas_limit = HeaderView ::new ( & chain . best_block_header ( ) ) . gas_limit ( ) ;
2016-07-13 19:59:59 +02:00
let mut queue = self . transaction_queue . lock ( ) ;
2016-03-17 15:20:33 +01:00
queue . set_gas_limit ( gas_limit ) ;
2016-10-10 23:04:43 +02:00
if let GasLimit ::Auto = self . options . tx_queue_gas_limit {
2016-10-14 13:56:00 +02:00
// Set total tx queue gas limit to be 20x the block gas limit.
queue . set_total_gas_limit ( gas_limit * 20. into ( ) ) ;
2016-10-10 23:04:43 +02:00
}
2016-03-17 12:47:31 +01:00
}
2016-04-28 17:36:53 +02:00
2016-09-13 15:09:07 +02:00
/// Returns true if we had to prepare new pending block.
fn prepare_work_sealing ( & self , chain : & MiningBlockChainClient ) -> bool {
trace! ( target : " miner " , " prepare_work_sealing: entering " ) ;
2016-07-28 21:06:36 +02:00
let prepare_new = {
let mut sealing_work = self . sealing_work . lock ( ) ;
let have_work = sealing_work . queue . peek_last_ref ( ) . is_some ( ) ;
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_work_sealing: have_work={} " , have_work ) ;
2016-07-28 21:06:36 +02:00
if ! have_work {
sealing_work . enabled = true ;
true
} else {
false
}
} ;
if prepare_new {
2016-07-08 17:19:14 +02:00
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
2016-09-13 15:09:07 +02:00
let ( block , original_work_hash ) = self . prepare_block ( chain ) ;
self . prepare_work ( block , original_work_hash ) ;
2016-04-28 17:36:53 +02:00
}
2016-07-13 19:59:59 +02:00
let mut sealing_block_last_request = self . sealing_block_last_request . lock ( ) ;
2016-04-28 17:36:53 +02:00
let best_number = chain . chain_info ( ) . best_block_number ;
if * sealing_block_last_request ! = best_number {
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " prepare_work_sealing: Miner received request (was {}, now {}) - waking up. " , * sealing_block_last_request , best_number ) ;
2016-04-28 17:36:53 +02:00
* sealing_block_last_request = best_number ;
}
2016-08-04 18:17:21 +02:00
// Return if we restarted
2016-07-28 21:06:36 +02:00
prepare_new
2016-04-28 17:36:53 +02:00
}
2016-06-29 16:26:19 +02:00
2016-11-17 11:01:21 +01:00
fn add_transactions_to_queue ( & self , chain : & MiningBlockChainClient , transactions : Vec < SignedTransaction > , default_origin : TransactionOrigin , transaction_queue : & mut BanningTransactionQueue ) ->
2016-07-06 17:15:59 +02:00
Vec < Result < TransactionImportResult , Error > > {
let fetch_account = | a : & Address | AccountDetails {
nonce : chain . latest_nonce ( a ) ,
balance : chain . latest_balance ( a ) ,
} ;
2016-11-17 11:01:21 +01:00
let accounts = self . accounts . as_ref ( )
. and_then ( | provider | provider . accounts ( ) . ok ( ) )
. map ( | accounts | accounts . into_iter ( ) . collect ::< HashSet < _ > > ( ) ) ;
2016-10-28 16:42:24 +02:00
let schedule = chain . latest_schedule ( ) ;
let gas_required = | tx : & SignedTransaction | tx . gas_required ( & schedule ) . into ( ) ;
2016-11-18 17:45:19 +01:00
let best_block_header : Header = ::rlp ::decode ( & chain . best_block_header ( ) ) ;
2016-07-06 17:15:59 +02:00
transactions . into_iter ( )
2016-11-23 08:46:55 +01:00
. map ( | tx | {
match self . engine . verify_transaction_basic ( & tx , & best_block_header ) {
2016-11-18 17:45:19 +01:00
Err ( e ) = > {
debug! ( target : " miner " , " Rejected tx {:?} with invalid signature: {:?} " , tx . hash ( ) , e ) ;
2016-11-23 08:46:55 +01:00
Err ( e )
} ,
Ok ( ( ) ) = > {
let origin = accounts . as_ref ( ) . and_then ( | accounts | {
tx . sender ( ) . ok ( ) . and_then ( | sender | match accounts . contains ( & sender ) {
true = > Some ( TransactionOrigin ::Local ) ,
false = > None ,
} )
} ) . unwrap_or ( default_origin ) ;
match origin {
TransactionOrigin ::Local | TransactionOrigin ::RetractedBlock = > {
transaction_queue . add ( tx , origin , & fetch_account , & gas_required )
} ,
TransactionOrigin ::External = > {
transaction_queue . add_with_banlist ( tx , & fetch_account , & gas_required )
}
}
2016-11-17 11:01:21 +01:00
} ,
2016-10-27 19:28:34 +02:00
}
} )
2016-07-06 17:15:59 +02:00
. collect ( )
}
2016-06-29 16:26:19 +02:00
/// Are we allowed to do a non-mandatory reseal?
2016-07-13 19:59:59 +02:00
fn tx_reseal_allowed ( & self ) -> bool { Instant ::now ( ) > * self . next_allowed_reseal . lock ( ) }
2016-10-07 12:13:15 +02:00
2016-10-27 08:28:12 +02:00
#[ cfg_attr(feature= " dev " , allow(wrong_self_convention)) ]
#[ cfg_attr(feature= " dev " , allow(redundant_closure)) ]
2016-10-07 12:13:15 +02:00
fn from_pending_block < H , F , G > ( & self , latest_block_number : BlockNumber , from_chain : F , map_block : G ) -> H
where F : Fn ( ) -> H , G : Fn ( & ClosedBlock ) -> H {
let sealing_work = self . sealing_work . lock ( ) ;
sealing_work . queue . peek_last_ref ( ) . map_or_else (
| | from_chain ( ) ,
| b | {
if b . block ( ) . header ( ) . number ( ) > latest_block_number {
map_block ( b )
} else {
from_chain ( )
}
}
)
}
2016-03-09 13:28:37 +01:00
}
2016-03-18 13:59:11 +01:00
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5 ;
2016-03-09 13:28:37 +01:00
impl MinerService for Miner {
2016-05-31 20:33:26 +02:00
fn clear_and_reset ( & self , chain : & MiningBlockChainClient ) {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . clear ( ) ;
2016-07-08 17:19:14 +02:00
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
2016-03-17 12:47:31 +01:00
self . update_sealing ( chain ) ;
2016-03-09 14:26:28 +01:00
}
2016-03-09 13:28:37 +01:00
fn status ( & self ) -> MinerStatus {
2016-07-13 19:59:59 +02:00
let status = self . transaction_queue . lock ( ) . status ( ) ;
let sealing_work = self . sealing_work . lock ( ) ;
2016-03-08 16:23:32 +01:00
MinerStatus {
2016-03-17 11:19:12 +01:00
transactions_in_pending_queue : status . pending ,
transactions_in_future_queue : status . future ,
2016-07-28 21:06:36 +02:00
transactions_in_pending_block : sealing_work . queue . peek_last_ref ( ) . map_or ( 0 , | b | b . transactions ( ) . len ( ) ) ,
2016-03-08 16:23:32 +01:00
}
}
2016-08-04 18:17:21 +02:00
fn call ( & self , chain : & MiningBlockChainClient , t : & SignedTransaction , analytics : CallAnalytics ) -> Result < Executed , CallError > {
2016-07-13 19:59:59 +02:00
let sealing_work = self . sealing_work . lock ( ) ;
2016-07-28 21:06:36 +02:00
match sealing_work . queue . peek_last_ref ( ) {
2016-04-28 21:47:44 +02:00
Some ( work ) = > {
let block = work . block ( ) ;
2016-06-05 17:23:27 +02:00
// TODO: merge this code with client.rs's fn call somwhow.
2016-04-28 21:47:44 +02:00
let header = block . header ( ) ;
2016-08-03 22:03:40 +02:00
let last_hashes = Arc ::new ( chain . last_hashes ( ) ) ;
2016-04-28 21:47:44 +02:00
let env_info = EnvInfo {
number : header . number ( ) ,
2016-05-02 13:13:12 +02:00
author : * header . author ( ) ,
2016-04-28 21:47:44 +02:00
timestamp : header . timestamp ( ) ,
2016-05-02 13:13:12 +02:00
difficulty : * header . difficulty ( ) ,
2016-04-28 21:47:44 +02:00
last_hashes : last_hashes ,
gas_used : U256 ::zero ( ) ,
gas_limit : U256 ::max_value ( ) ,
} ;
// that's just a copy of the state.
let mut state = block . state ( ) . clone ( ) ;
2016-08-04 18:17:21 +02:00
let original_state = if analytics . state_diffing { Some ( state . clone ( ) ) } else { None } ;
2016-05-14 14:28:44 +02:00
let sender = try ! ( t . sender ( ) . map_err ( | e | {
let message = format! ( " Transaction malformed: {:?} " , e ) ;
ExecutionError ::TransactionMalformed ( message )
} ) ) ;
2016-04-28 21:47:44 +02:00
let balance = state . balance ( & sender ) ;
2016-06-05 17:23:27 +02:00
let needed_balance = t . value + t . gas * t . gas_price ;
if balance < needed_balance {
// give the sender a sufficient balance
2016-11-03 22:22:25 +01:00
state . add_balance ( & sender , & ( needed_balance - balance ) , CleanupMode ::NoEmpty ) ;
2016-06-05 17:23:27 +02:00
}
2016-06-02 13:50:50 +02:00
let options = TransactOptions { tracing : analytics . transaction_tracing , vm_tracing : analytics . vm_tracing , check_nonce : false } ;
2016-08-05 23:33:55 +02:00
let mut ret = try ! ( Executive ::new ( & mut state , & env_info , & * self . engine , chain . vm_factory ( ) ) . transact ( t , options ) ) ;
2016-06-06 14:33:12 +02:00
2016-05-31 21:03:44 +02:00
// TODO gav move this into Executive.
2016-08-04 18:17:21 +02:00
ret . state_diff = original_state . map ( | original | state . diff_from ( original ) ) ;
Ok ( ret )
2016-04-28 21:47:44 +02:00
} ,
None = > {
2016-08-04 18:17:21 +02:00
chain . call ( t , BlockID ::Latest , analytics )
2016-04-28 21:47:44 +02:00
}
}
}
2016-05-31 20:33:26 +02:00
fn balance ( & self , chain : & MiningBlockChainClient , address : & Address ) -> U256 {
2016-10-07 12:13:15 +02:00
self . from_pending_block (
chain . chain_info ( ) . best_block_number ,
2016-05-26 22:17:55 +02:00
| | chain . latest_balance ( address ) ,
2016-05-26 11:46:45 +02:00
| b | b . block ( ) . fields ( ) . state . balance ( address )
)
2016-04-28 21:47:44 +02:00
}
2016-05-31 20:33:26 +02:00
fn storage_at ( & self , chain : & MiningBlockChainClient , address : & Address , position : & H256 ) -> H256 {
2016-10-07 12:13:15 +02:00
self . from_pending_block (
chain . chain_info ( ) . best_block_number ,
2016-05-26 22:17:55 +02:00
| | chain . latest_storage_at ( address , position ) ,
2016-05-26 11:46:45 +02:00
| b | b . block ( ) . fields ( ) . state . storage_at ( address , position )
)
2016-04-28 21:47:44 +02:00
}
2016-05-31 20:33:26 +02:00
fn nonce ( & self , chain : & MiningBlockChainClient , address : & Address ) -> U256 {
2016-10-07 12:13:15 +02:00
self . from_pending_block (
chain . chain_info ( ) . best_block_number ,
| | chain . latest_nonce ( address ) ,
| b | b . block ( ) . fields ( ) . state . nonce ( address )
)
2016-04-28 21:47:44 +02:00
}
2016-05-31 20:33:26 +02:00
fn code ( & self , chain : & MiningBlockChainClient , address : & Address ) -> Option < Bytes > {
2016-10-07 12:13:15 +02:00
self . from_pending_block (
chain . chain_info ( ) . best_block_number ,
| | chain . latest_code ( address ) ,
| b | b . block ( ) . fields ( ) . state . code ( address ) . map ( | c | ( * c ) . clone ( ) )
)
2016-04-28 21:47:44 +02:00
}
2016-04-13 00:04:40 +02:00
fn set_author ( & self , author : Address ) {
2016-09-15 12:12:15 +02:00
if self . seals_internally {
let mut sealing_work = self . sealing_work . lock ( ) ;
sealing_work . enabled = self . engine . is_sealer ( & author ) . unwrap_or ( false ) ;
}
2016-07-13 19:59:59 +02:00
* self . author . write ( ) = author ;
2016-04-13 00:04:40 +02:00
}
2016-12-05 22:31:38 +01:00
fn set_engine_signer ( & self , address : Address , password : String ) -> Result < ( ) , AccountError > {
2016-12-05 18:08:16 +01:00
if self . seals_internally {
if let Some ( ref ap ) = self . accounts {
try ! ( ap . sign ( address . clone ( ) , Some ( password . clone ( ) ) , Default ::default ( ) ) ) ;
}
let mut sealing_work = self . sealing_work . lock ( ) ;
sealing_work . enabled = self . engine . is_sealer ( & address ) . unwrap_or ( false ) ;
* self . author . write ( ) = address ;
self . engine . set_signer ( address , password ) ;
}
Ok ( ( ) )
}
2016-04-13 00:04:40 +02:00
fn set_extra_data ( & self , extra_data : Bytes ) {
2016-07-13 19:59:59 +02:00
* self . extra_data . write ( ) = extra_data ;
2016-04-13 00:04:40 +02:00
}
/// Set the gas limit we wish to target when sealing a new block.
fn set_gas_floor_target ( & self , target : U256 ) {
2016-07-13 19:59:59 +02:00
self . gas_range_target . write ( ) . 0 = target ;
2016-06-23 14:29:16 +02:00
}
fn set_gas_ceil_target ( & self , target : U256 ) {
2016-07-13 19:59:59 +02:00
self . gas_range_target . write ( ) . 1 = target ;
2016-04-13 00:04:40 +02:00
}
fn set_minimal_gas_price ( & self , min_gas_price : U256 ) {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . set_minimal_gas_price ( min_gas_price ) ;
2016-04-13 00:04:40 +02:00
}
fn minimal_gas_price ( & self ) -> U256 {
2016-07-13 19:59:59 +02:00
* self . transaction_queue . lock ( ) . minimal_gas_price ( )
2016-04-13 00:04:40 +02:00
}
2016-03-28 18:53:33 +02:00
fn sensible_gas_price ( & self ) -> U256 {
// 10% above our minimum.
2016-07-13 19:59:59 +02:00
* self . transaction_queue . lock ( ) . minimal_gas_price ( ) * 110. into ( ) / 100. into ( )
2016-03-28 18:53:33 +02:00
}
2016-04-14 21:01:12 +02:00
fn sensible_gas_limit ( & self ) -> U256 {
2016-07-13 19:59:59 +02:00
self . gas_range_target . read ( ) . 0 / 5. into ( )
2016-04-14 21:01:12 +02:00
}
2016-04-18 23:03:41 +02:00
fn transactions_limit ( & self ) -> usize {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . limit ( )
2016-04-18 23:03:41 +02:00
}
fn set_transactions_limit ( & self , limit : usize ) {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . set_limit ( limit )
2016-04-18 23:03:41 +02:00
}
2016-06-28 10:21:29 +02:00
fn set_tx_gas_limit ( & self , limit : U256 ) {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . set_tx_gas_limit ( limit )
2016-06-27 20:19:01 +02:00
}
2016-04-11 21:06:32 +02:00
/// Get the author that we will seal blocks as.
2016-03-22 19:12:17 +01:00
fn author ( & self ) -> Address {
2016-07-13 19:59:59 +02:00
* self . author . read ( )
2016-03-22 19:12:17 +01:00
}
2016-04-11 21:06:32 +02:00
/// Get the extra_data that we will seal blocks with.
2016-03-22 19:12:17 +01:00
fn extra_data ( & self ) -> Bytes {
2016-07-13 19:59:59 +02:00
self . extra_data . read ( ) . clone ( )
2016-03-22 19:12:17 +01:00
}
2016-04-11 21:06:32 +02:00
/// Get the gas limit we wish to target when sealing a new block.
fn gas_floor_target ( & self ) -> U256 {
2016-07-13 19:59:59 +02:00
self . gas_range_target . read ( ) . 0
2016-06-23 14:29:16 +02:00
}
/// Get the gas limit we wish to target when sealing a new block.
fn gas_ceil_target ( & self ) -> U256 {
2016-07-13 19:59:59 +02:00
self . gas_range_target . read ( ) . 1
2016-04-11 21:06:32 +02:00
}
2016-07-08 17:26:06 +02:00
fn import_external_transactions (
& self ,
chain : & MiningBlockChainClient ,
transactions : Vec < SignedTransaction >
) -> Vec < Result < TransactionImportResult , Error > > {
2016-09-19 10:38:47 +02:00
trace! ( target : " external_tx " , " Importing external transactions " ) ;
2016-07-08 17:26:06 +02:00
let results = {
2016-07-13 19:59:59 +02:00
let mut transaction_queue = self . transaction_queue . lock ( ) ;
2016-07-08 17:19:14 +02:00
self . add_transactions_to_queue (
chain , transactions , TransactionOrigin ::External , & mut transaction_queue
)
2016-07-08 17:26:06 +02:00
} ;
2016-07-06 17:15:59 +02:00
if ! results . is_empty ( ) & & self . options . reseal_on_external_tx & & self . tx_reseal_allowed ( ) {
2016-07-08 17:19:14 +02:00
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
2016-06-21 16:00:34 +02:00
self . update_sealing ( chain ) ;
}
results
2016-03-08 15:46:44 +01:00
}
2016-09-16 23:03:26 +02:00
#[ cfg_attr(feature= " dev " , allow(collapsible_if)) ]
2016-07-06 17:15:59 +02:00
fn import_own_transaction (
2016-06-19 14:51:51 +02:00
& self ,
chain : & MiningBlockChainClient ,
transaction : SignedTransaction ,
2016-07-06 17:15:59 +02:00
) -> Result < TransactionImportResult , Error > {
2016-06-19 14:51:51 +02:00
2016-04-17 20:36:37 +02:00
let hash = transaction . hash ( ) ;
trace! ( target : " own_tx " , " Importing transaction: {:?} " , transaction ) ;
2016-04-28 17:36:53 +02:00
let imported = {
2016-09-13 15:09:07 +02:00
// Be sure to release the lock before we call prepare_work_sealing
2016-07-13 19:59:59 +02:00
let mut transaction_queue = self . transaction_queue . lock ( ) ;
2016-07-14 12:16:53 +02:00
let import = self . add_transactions_to_queue (
chain , vec! [ transaction ] , TransactionOrigin ::Local , & mut transaction_queue
2016-10-20 23:41:15 +02:00
) . pop ( ) . expect ( " one result returned per added transaction; one added => one result; qed " ) ;
2016-04-28 17:36:53 +02:00
match import {
Ok ( ref res ) = > {
trace! ( target : " own_tx " , " Imported transaction to {:?} (hash: {:?}) " , res , hash ) ;
trace! ( target : " own_tx " , " Status: {:?} " , transaction_queue . status ( ) ) ;
} ,
Err ( ref e ) = > {
trace! ( target : " own_tx " , " Failed to import transaction {:?} (hash: {:?}) " , e , hash ) ;
trace! ( target : " own_tx " , " Status: {:?} " , transaction_queue . status ( ) ) ;
2016-05-30 13:13:48 +02:00
warn! ( target : " own_tx " , " Error importing transaction: {:?} " , e ) ;
2016-04-28 17:36:53 +02:00
} ,
}
import
} ;
2016-07-08 17:19:14 +02:00
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
2016-06-29 16:26:19 +02:00
if imported . is_ok ( ) & & self . options . reseal_on_own_tx & & self . tx_reseal_allowed ( ) {
2016-04-28 17:36:53 +02:00
// Make sure to do it after transaction is imported and lock is droped.
2016-09-13 15:09:07 +02:00
// We need to create pending block and enable sealing.
2016-09-15 12:12:15 +02:00
if self . seals_internally | | ! self . prepare_work_sealing ( chain ) {
2016-09-13 15:09:07 +02:00
// If new block has not been prepared (means we already had one)
// or Engine might be able to seal internally,
// we need to update sealing.
2016-04-28 17:36:53 +02:00
self . update_sealing ( chain ) ;
}
2016-04-17 20:36:37 +02:00
}
2016-04-28 17:36:53 +02:00
imported
2016-04-17 18:26:15 +02:00
}
2016-06-27 19:06:54 +02:00
fn all_transactions ( & self ) -> Vec < SignedTransaction > {
2016-07-13 19:59:59 +02:00
let queue = self . transaction_queue . lock ( ) ;
2016-06-27 19:06:54 +02:00
queue . top_transactions ( )
2016-03-10 16:00:55 +01:00
}
2016-11-16 17:54:54 +01:00
fn local_transactions ( & self ) -> BTreeMap < H256 , LocalTransactionStatus > {
let queue = self . transaction_queue . lock ( ) ;
queue . local_transactions ( )
. iter ( )
. map ( | ( hash , status ) | ( * hash , status . clone ( ) ) )
. collect ( )
}
2016-10-07 12:13:15 +02:00
fn pending_transactions ( & self , best_block : BlockNumber ) -> Vec < SignedTransaction > {
2016-07-13 19:59:59 +02:00
let queue = self . transaction_queue . lock ( ) ;
2016-10-07 12:13:15 +02:00
match self . options . pending_set {
PendingSet ::AlwaysQueue = > queue . top_transactions ( ) ,
PendingSet ::SealingOrElseQueue = > {
self . from_pending_block (
best_block ,
| | queue . top_transactions ( ) ,
| sealing | sealing . transactions ( ) . to_owned ( )
)
} ,
PendingSet ::AlwaysSealing = > {
self . from_pending_block (
best_block ,
| | vec! [ ] ,
| sealing | sealing . transactions ( ) . to_owned ( )
)
} ,
2016-05-24 21:56:32 +02:00
}
2016-03-27 15:12:21 +02:00
}
2016-10-07 12:13:15 +02:00
fn pending_transactions_hashes ( & self , best_block : BlockNumber ) -> Vec < H256 > {
2016-07-13 19:59:59 +02:00
let queue = self . transaction_queue . lock ( ) ;
2016-10-07 12:13:15 +02:00
match self . options . pending_set {
PendingSet ::AlwaysQueue = > queue . pending_hashes ( ) ,
PendingSet ::SealingOrElseQueue = > {
self . from_pending_block (
best_block ,
| | queue . pending_hashes ( ) ,
| sealing | sealing . transactions ( ) . iter ( ) . map ( | t | t . hash ( ) ) . collect ( )
)
} ,
PendingSet ::AlwaysSealing = > {
self . from_pending_block (
best_block ,
| | vec! [ ] ,
| sealing | sealing . transactions ( ) . iter ( ) . map ( | t | t . hash ( ) ) . collect ( )
)
} ,
2016-06-27 19:06:54 +02:00
}
2016-04-06 23:03:07 +02:00
}
2016-10-07 12:13:15 +02:00
fn transaction ( & self , best_block : BlockNumber , hash : & H256 ) -> Option < SignedTransaction > {
2016-07-13 19:59:59 +02:00
let queue = self . transaction_queue . lock ( ) ;
2016-10-07 12:13:15 +02:00
match self . options . pending_set {
PendingSet ::AlwaysQueue = > queue . find ( hash ) ,
PendingSet ::SealingOrElseQueue = > {
self . from_pending_block (
best_block ,
| | queue . find ( hash ) ,
| sealing | sealing . transactions ( ) . iter ( ) . find ( | t | & t . hash ( ) = = hash ) . cloned ( )
)
} ,
PendingSet ::AlwaysSealing = > {
self . from_pending_block (
best_block ,
| | None ,
| sealing | sealing . transactions ( ) . iter ( ) . find ( | t | & t . hash ( ) = = hash ) . cloned ( )
)
} ,
2016-05-24 21:56:32 +02:00
}
}
2016-10-07 12:13:15 +02:00
fn pending_receipt ( & self , best_block : BlockNumber , hash : & H256 ) -> Option < RichReceipt > {
self . from_pending_block (
best_block ,
| | None ,
| pending | {
2016-08-17 19:25:02 +02:00
let txs = pending . transactions ( ) ;
txs . iter ( )
. map ( | t | t . hash ( ) )
. position ( | t | t = = * hash )
. map ( | index | {
let prev_gas = if index = = 0 { Default ::default ( ) } else { pending . receipts ( ) [ index - 1 ] . gas_used } ;
2016-08-23 19:28:21 +02:00
let tx = & txs [ index ] ;
let receipt = & pending . receipts ( ) [ index ] ;
2016-08-17 19:25:02 +02:00
RichReceipt {
transaction_hash : hash . clone ( ) ,
transaction_index : index ,
cumulative_gas_used : receipt . gas_used ,
gas_used : receipt . gas_used - prev_gas ,
contract_address : match tx . action {
Action ::Call ( _ ) = > None ,
2016-10-20 23:41:15 +02:00
Action ::Create = > {
let sender = tx . sender ( )
. expect ( " transactions in pending block have already been checked for valid sender; qed " ) ;
Some ( contract_address ( & sender , & tx . nonce ) )
}
2016-08-17 19:25:02 +02:00
} ,
logs : receipt . logs . clone ( ) ,
2016-11-04 12:33:13 +01:00
log_bloom : receipt . log_bloom ,
state_root : receipt . state_root ,
2016-08-17 19:25:02 +02:00
}
} )
2016-10-07 12:13:15 +02:00
}
)
2016-08-17 19:25:02 +02:00
}
2016-10-07 12:13:15 +02:00
fn pending_receipts ( & self , best_block : BlockNumber ) -> BTreeMap < H256 , Receipt > {
self . from_pending_block (
best_block ,
2016-10-27 08:28:12 +02:00
BTreeMap ::new ,
2016-10-07 12:13:15 +02:00
| pending | {
2016-05-24 21:56:32 +02:00
let hashes = pending . transactions ( )
. iter ( )
. map ( | t | t . hash ( ) ) ;
2016-07-12 10:28:35 +02:00
let receipts = pending . receipts ( ) . iter ( ) . cloned ( ) ;
2016-05-24 21:56:32 +02:00
hashes . zip ( receipts ) . collect ( )
2016-10-07 12:13:15 +02:00
}
)
2016-05-24 21:56:32 +02:00
}
2016-04-06 12:15:20 +02:00
fn last_nonce ( & self , address : & Address ) -> Option < U256 > {
2016-07-13 19:59:59 +02:00
self . transaction_queue . lock ( ) . last_nonce ( address )
2016-04-06 12:15:20 +02:00
}
2016-09-13 15:09:07 +02:00
/// Update sealing if required.
/// Prepare the block and work if the Engine does not seal internally.
2016-05-31 20:33:26 +02:00
fn update_sealing ( & self , chain : & MiningBlockChainClient ) {
2016-07-28 21:06:36 +02:00
trace! ( target : " miner " , " update_sealing " ) ;
2016-08-04 18:17:21 +02:00
2016-09-13 15:09:07 +02:00
if self . requires_reseal ( chain . chain_info ( ) . best_block_number ) {
2016-07-28 21:06:36 +02:00
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " update_sealing: preparing a block " ) ;
let ( block , original_work_hash ) = self . prepare_block ( chain ) ;
2016-09-15 12:12:15 +02:00
if self . seals_internally {
2016-09-13 15:09:07 +02:00
trace! ( target : " miner " , " update_sealing: engine indicates internal sealing " ) ;
self . seal_and_import_block_internally ( chain , block ) ;
} else {
trace! ( target : " miner " , " update_sealing: engine does not seal internally, preparing work " ) ;
self . prepare_work ( block , original_work_hash ) ;
}
2016-03-17 12:47:31 +01:00
}
2016-03-08 15:46:44 +01:00
}
2016-08-02 18:53:32 +02:00
fn is_sealing ( & self ) -> bool {
self . sealing_work . lock ( ) . queue . is_in_use ( )
}
2016-05-31 20:33:26 +02:00
fn map_sealing_work < F , T > ( & self , chain : & MiningBlockChainClient , f : F ) -> Option < T > where F : FnOnce ( & ClosedBlock ) -> T {
2016-03-26 20:36:03 +01:00
trace! ( target : " miner " , " map_sealing_work: entering " ) ;
2016-09-13 15:09:07 +02:00
self . prepare_work_sealing ( chain ) ;
2016-04-28 17:36:53 +02:00
trace! ( target : " miner " , " map_sealing_work: sealing prepared " ) ;
2016-07-13 19:59:59 +02:00
let mut sealing_work = self . sealing_work . lock ( ) ;
2016-07-28 21:06:36 +02:00
let ret = sealing_work . queue . use_last_ref ( ) ;
2016-03-26 20:36:03 +01:00
trace! ( target : " miner " , " map_sealing_work: leaving use_last_ref={:?} " , ret . as_ref ( ) . map ( | b | b . block ( ) . fields ( ) . header . hash ( ) ) ) ;
ret . map ( f )
2016-03-08 15:46:44 +01:00
}
2016-12-05 18:08:16 +01:00
fn submit_seal ( & self , chain : & MiningBlockChainClient , block_hash : H256 , seal : Vec < Bytes > ) -> Result < ( ) , Error > {
2016-09-28 23:31:59 +02:00
let result =
if let Some ( b ) = self . sealing_work . lock ( ) . queue . get_used_if (
if self . options . enable_resubmission {
GetAction ::Clone
} else {
GetAction ::Take
} ,
2016-12-05 18:08:16 +01:00
| b | & b . hash ( ) = = & block_hash
2016-09-28 23:31:59 +02:00
) {
2016-12-05 18:08:16 +01:00
trace! ( target : " miner " , " Submitted block {}={}={} with seal {:?} " , block_hash , b . hash ( ) , b . header ( ) . bare_hash ( ) , seal ) ;
2016-09-28 23:31:59 +02:00
b . lock ( ) . try_seal ( & * self . engine , seal ) . or_else ( | ( e , _ ) | {
warn! ( target : " miner " , " Mined solution rejected: {} " , e ) ;
Err ( Error ::PowInvalid )
} )
} else {
2016-12-05 18:08:16 +01:00
warn! ( target : " miner " , " Submitted solution rejected: Block unknown or out of date. " ) ;
2016-09-28 23:31:59 +02:00
Err ( Error ::PowHashInvalid )
} ;
2016-06-29 21:49:12 +02:00
result . and_then ( | sealed | {
2016-06-29 16:45:17 +02:00
let n = sealed . header ( ) . number ( ) ;
let h = sealed . header ( ) . hash ( ) ;
2016-06-29 21:49:12 +02:00
try ! ( chain . import_sealed_block ( sealed ) ) ;
2016-12-05 18:08:16 +01:00
info! ( target : " miner " , " Submitted block imported OK. #{}: {} " , Colour ::White . bold ( ) . paint ( format! ( " {} " , n ) ) , Colour ::White . bold ( ) . paint ( h . hex ( ) ) ) ;
2016-06-29 21:49:12 +02:00
Ok ( ( ) )
} )
2016-03-08 15:46:44 +01:00
}
2016-05-31 20:33:26 +02:00
fn chain_new_blocks ( & self , chain : & MiningBlockChainClient , _imported : & [ H256 ] , _invalid : & [ H256 ] , enacted : & [ H256 ] , retracted : & [ H256 ] ) {
2016-07-28 21:06:36 +02:00
trace! ( target : " miner " , " chain_new_blocks " ) ;
2016-04-15 07:38:23 +02:00
// 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions
// should be still available in the queue.
// 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that
// are in those blocks
2016-03-17 15:20:33 +01:00
// First update gas limit in transaction queue
self . update_gas_limit ( chain ) ;
// Then import all transactions...
2016-03-08 15:46:44 +01:00
{
2016-12-09 15:54:13 +01:00
retracted . par_iter ( )
. map ( | hash | {
let block = chain . block ( BlockID ::Hash ( * hash ) )
. expect ( " Client is sending message after commit to db and inserting to chain; the block is available; qed " ) ;
let block = BlockView ::new ( & block ) ;
let txs = block . transactions ( ) ;
// populate sender
for tx in & txs {
let _sender = tx . sender ( ) ;
}
txs
} ) . for_each ( | txs | {
let mut transaction_queue = self . transaction_queue . lock ( ) ;
let _ = self . add_transactions_to_queue (
chain , txs , TransactionOrigin ::RetractedBlock , & mut transaction_queue
) ;
} ) ;
2016-03-08 15:46:44 +01:00
}
2016-03-17 15:20:33 +01:00
2016-12-09 15:54:13 +01:00
// ...and at the end remove the old ones
2016-03-15 23:01:36 +01:00
{
2016-12-09 15:54:13 +01:00
let mut transaction_queue = self . transaction_queue . lock ( ) ;
transaction_queue . remove_old ( | sender | chain . latest_nonce ( sender ) ) ;
2016-03-08 15:46:44 +01:00
}
2016-07-28 21:06:36 +02:00
if enacted . len ( ) > 0 {
// --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self . update_sealing ( chain ) ;
}
2016-03-08 15:46:44 +01:00
}
}
2016-03-18 13:59:11 +01:00
#[ cfg(test) ]
mod tests {
2016-11-28 17:05:37 +01:00
use std ::sync ::Arc ;
2016-07-14 12:16:53 +02:00
use std ::time ::Duration ;
2016-10-15 14:46:33 +02:00
use super ::super ::{ MinerService , PrioritizationStrategy } ;
2016-07-14 12:16:53 +02:00
use super ::* ;
2016-11-28 17:05:37 +01:00
use block ::IsBlock ;
use util ::{ U256 , Uint , FromHex } ;
2016-08-24 18:35:21 +02:00
use ethkey ::{ Generator , Random } ;
2016-09-13 15:09:07 +02:00
use client ::{ BlockChainClient , TestBlockChainClient , EachBlockWith , TransactionImportResult } ;
use header ::BlockNumber ;
use types ::transaction ::{ Transaction , SignedTransaction , Action } ;
2016-06-23 21:04:23 +02:00
use spec ::Spec ;
2016-09-13 15:09:07 +02:00
use tests ::helpers ::{ generate_dummy_client } ;
2016-03-18 13:59:11 +01:00
#[ test ]
fn should_prepare_block_to_seal ( ) {
// given
let client = TestBlockChainClient ::default ( ) ;
2016-08-05 23:33:55 +02:00
let miner = Miner ::with_spec ( & Spec ::new_test ( ) ) ;
2016-03-18 13:59:11 +01:00
// when
2016-03-25 16:41:01 +01:00
let sealing_work = miner . map_sealing_work ( & client , | _ | ( ) ) ;
assert! ( sealing_work . is_some ( ) , " Expected closed block " ) ;
2016-03-18 13:59:11 +01:00
}
#[ test ]
2016-03-25 16:41:01 +01:00
fn should_still_work_after_a_couple_of_blocks ( ) {
2016-03-18 13:59:11 +01:00
// given
let client = TestBlockChainClient ::default ( ) ;
2016-08-05 23:33:55 +02:00
let miner = Miner ::with_spec ( & Spec ::new_test ( ) ) ;
2016-03-18 13:59:11 +01:00
2016-03-25 16:41:01 +01:00
let res = miner . map_sealing_work ( & client , | b | b . block ( ) . fields ( ) . header . hash ( ) ) ;
assert! ( res . is_some ( ) ) ;
assert! ( miner . submit_seal ( & client , res . unwrap ( ) , vec! [ ] ) . is_ok ( ) ) ;
// two more blocks mined, work requested.
client . add_blocks ( 1 , EachBlockWith ::Uncle ) ;
miner . map_sealing_work ( & client , | b | b . block ( ) . fields ( ) . header . hash ( ) ) ;
client . add_blocks ( 1 , EachBlockWith ::Uncle ) ;
miner . map_sealing_work ( & client , | b | b . block ( ) . fields ( ) . header . hash ( ) ) ;
2016-03-18 13:59:11 +01:00
2016-03-25 16:41:01 +01:00
// solution to original work submitted.
assert! ( miner . submit_seal ( & client , res . unwrap ( ) , vec! [ ] ) . is_ok ( ) ) ;
2016-03-18 13:59:11 +01:00
}
2016-07-14 12:16:53 +02:00
fn miner ( ) -> Miner {
Arc ::try_unwrap ( Miner ::new (
MinerOptions {
new_work_notify : Vec ::new ( ) ,
force_sealing : false ,
reseal_on_external_tx : false ,
reseal_on_own_tx : true ,
reseal_min_period : Duration ::from_secs ( 5 ) ,
tx_gas_limit : ! U256 ::zero ( ) ,
tx_queue_size : 1024 ,
2016-10-10 23:04:43 +02:00
tx_queue_gas_limit : GasLimit ::None ,
2016-10-15 14:46:33 +02:00
tx_queue_strategy : PrioritizationStrategy ::GasFactorAndGasPrice ,
2016-07-14 12:16:53 +02:00
pending_set : PendingSet ::AlwaysSealing ,
work_queue_size : 5 ,
enable_resubmission : true ,
2016-10-27 19:28:34 +02:00
tx_queue_banning : Banning ::Disabled ,
2016-07-14 12:16:53 +02:00
} ,
GasPricer ::new_fixed ( 0 u64 . into ( ) ) ,
2016-08-05 23:33:55 +02:00
& Spec ::new_test ( ) ,
2016-07-14 12:16:53 +02:00
None , // accounts provider
) ) . ok ( ) . expect ( " Miner was just created. " )
}
2016-09-13 15:09:07 +02:00
fn transaction ( ) -> SignedTransaction {
let keypair = Random . generate ( ) . unwrap ( ) ;
Transaction {
action : Action ::Create ,
value : U256 ::zero ( ) ,
data : " 3331600055 " . from_hex ( ) . unwrap ( ) ,
gas : U256 ::from ( 100_000 ) ,
gas_price : U256 ::zero ( ) ,
nonce : U256 ::zero ( ) ,
2016-11-03 22:22:25 +01:00
} . sign ( keypair . secret ( ) , None )
2016-09-13 15:09:07 +02:00
}
2016-07-14 12:16:53 +02:00
#[ test ]
fn should_make_pending_block_when_importing_own_transaction ( ) {
// given
let client = TestBlockChainClient ::default ( ) ;
let miner = miner ( ) ;
2016-09-13 15:09:07 +02:00
let transaction = transaction ( ) ;
2016-10-07 12:13:15 +02:00
let best_block = 0 ;
2016-07-14 12:16:53 +02:00
// when
let res = miner . import_own_transaction ( & client , transaction ) ;
// then
assert_eq! ( res . unwrap ( ) , TransactionImportResult ::Current ) ;
assert_eq! ( miner . all_transactions ( ) . len ( ) , 1 ) ;
2016-10-07 12:13:15 +02:00
assert_eq! ( miner . pending_transactions ( best_block ) . len ( ) , 1 ) ;
assert_eq! ( miner . pending_transactions_hashes ( best_block ) . len ( ) , 1 ) ;
assert_eq! ( miner . pending_receipts ( best_block ) . len ( ) , 1 ) ;
2016-07-14 12:16:53 +02:00
// This method will let us know if pending block was created (before calling that method)
2016-09-15 12:12:15 +02:00
assert! ( ! miner . prepare_work_sealing ( & client ) ) ;
2016-07-14 12:16:53 +02:00
}
2016-10-07 12:13:15 +02:00
#[ test ]
fn should_not_use_pending_block_if_best_block_is_higher ( ) {
// given
let client = TestBlockChainClient ::default ( ) ;
let miner = miner ( ) ;
let transaction = transaction ( ) ;
let best_block = 10 ;
// when
let res = miner . import_own_transaction ( & client , transaction ) ;
// then
assert_eq! ( res . unwrap ( ) , TransactionImportResult ::Current ) ;
assert_eq! ( miner . all_transactions ( ) . len ( ) , 1 ) ;
assert_eq! ( miner . pending_transactions ( best_block ) . len ( ) , 0 ) ;
assert_eq! ( miner . pending_transactions_hashes ( best_block ) . len ( ) , 0 ) ;
assert_eq! ( miner . pending_receipts ( best_block ) . len ( ) , 0 ) ;
}
2016-07-14 12:16:53 +02:00
#[ test ]
fn should_import_external_transaction ( ) {
// given
let client = TestBlockChainClient ::default ( ) ;
let miner = miner ( ) ;
2016-09-13 15:09:07 +02:00
let transaction = transaction ( ) ;
2016-10-07 12:13:15 +02:00
let best_block = 0 ;
2016-07-14 12:16:53 +02:00
// when
let res = miner . import_external_transactions ( & client , vec! [ transaction ] ) . pop ( ) . unwrap ( ) ;
// then
assert_eq! ( res . unwrap ( ) , TransactionImportResult ::Current ) ;
assert_eq! ( miner . all_transactions ( ) . len ( ) , 1 ) ;
2016-10-07 12:13:15 +02:00
assert_eq! ( miner . pending_transactions_hashes ( best_block ) . len ( ) , 0 ) ;
assert_eq! ( miner . pending_transactions ( best_block ) . len ( ) , 0 ) ;
assert_eq! ( miner . pending_receipts ( best_block ) . len ( ) , 0 ) ;
2016-07-14 12:16:53 +02:00
// This method will let us know if pending block was created (before calling that method)
2016-09-15 12:12:15 +02:00
assert! ( miner . prepare_work_sealing ( & client ) ) ;
}
#[ test ]
fn should_not_seal_unless_enabled ( ) {
let miner = miner ( ) ;
let client = TestBlockChainClient ::default ( ) ;
// By default resealing is not required.
assert! ( ! miner . requires_reseal ( 1 u8 . into ( ) ) ) ;
miner . import_external_transactions ( & client , vec! [ transaction ( ) ] ) . pop ( ) . unwrap ( ) . unwrap ( ) ;
assert! ( miner . prepare_work_sealing ( & client ) ) ;
// Unless asked to prepare work.
assert! ( miner . requires_reseal ( 1 u8 . into ( ) ) ) ;
2016-09-13 15:09:07 +02:00
}
#[ test ]
fn internal_seals_without_work ( ) {
2016-11-11 18:27:20 +01:00
let miner = Miner ::with_spec ( & Spec ::new_instant ( ) ) ;
2016-09-15 12:12:15 +02:00
2016-09-13 15:09:07 +02:00
let c = generate_dummy_client ( 2 ) ;
let client = c . reference ( ) . as_ref ( ) ;
assert_eq! ( miner . import_external_transactions ( client , vec! [ transaction ( ) ] ) . pop ( ) . unwrap ( ) . unwrap ( ) , TransactionImportResult ::Current ) ;
miner . update_sealing ( client ) ;
client . flush_queue ( ) ;
assert! ( miner . pending_block ( ) . is_none ( ) ) ;
assert_eq! ( client . chain_info ( ) . best_block_number , 3 as BlockNumber ) ;
assert_eq! ( miner . import_own_transaction ( client , transaction ( ) ) . unwrap ( ) , TransactionImportResult ::Current ) ;
miner . update_sealing ( client ) ;
client . flush_queue ( ) ;
assert! ( miner . pending_block ( ) . is_none ( ) ) ;
assert_eq! ( client . chain_info ( ) . best_block_number , 4 as BlockNumber ) ;
2016-07-14 12:16:53 +02:00
}
2016-03-18 13:59:11 +01:00
}