Improve logging and cleanup in miner around block sealing (#10745)
* Stop breaking out of loop if a non-canonical hash is found * include expected hash in log msg * More logging * Scope * Syntax * Log in blank RollingFinality Escalate bad proposer to warning * Check validator set size: warn if 1 or even number * More readable code * Use SimpleList::new * Extensive logging on unexpected non-canonical hash * Wording * wip * Update ethcore/blockchain/src/blockchain.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Improved logging, address grumbles * Update ethcore/src/engines/validator_set/simple_list.rs Co-Authored-By: Luke Schoen <ltfschoen@users.noreply.github.com> * Report benign misbehaviour iff currently a validator * Report malicious behaviour iff we're a validator * Escalate to warning and fix wording * Test reporting behaviour Don't require node to be part of the validator set to report malicious behaviour * Include missing parent hash in MissingParent error * Update ethcore/src/engines/validator_set/simple_list.rs Co-Authored-By: Luke Schoen <ltfschoen@users.noreply.github.com> * docs * remove unneeded into() Move check for parent_step == step for clarity&efficiency Remove dead code for Seal::Proposal * typo * Wording * naming * WIP * cleanup * cosmetics * cosmetics and one less lvar * spelling * Better loggin when a block is already in chain * More logging * On second thought non-validators are allowed to report * cleanup * remove dead code * Keep track of the hash of the last imported block * Let it lock * Serialize access to block sealing * Take a lock while sealing a block * Cleanup * whitespace
This commit is contained in:
@@ -146,7 +146,7 @@ pub struct MinerOptions {
|
||||
pub tx_queue_strategy: PrioritizationStrategy,
|
||||
/// Simple senders penalization.
|
||||
pub tx_queue_penalization: Penalization,
|
||||
/// Do we want to mark transactions recieved locally (e.g. RPC) as local if we don't have the sending account?
|
||||
/// Do we want to mark transactions received locally (e.g. RPC) as local if we don't have the sending account?
|
||||
pub tx_queue_no_unfamiliar_locals: bool,
|
||||
/// Do we refuse to accept service transactions even if sender is certified.
|
||||
pub refuse_service_transactions: bool,
|
||||
@@ -231,6 +231,10 @@ impl SealingWork {
|
||||
fn reseal_allowed(&self) -> bool {
|
||||
Instant::now() > self.next_allowed_reseal
|
||||
}
|
||||
|
||||
fn work_available(&self) -> bool {
|
||||
self.queue.peek_last_ref().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps track of transactions using priority queue and holds currently mined block.
|
||||
@@ -620,6 +624,7 @@ impl Miner {
|
||||
|
||||
trace!(target: "miner", "requires_reseal: sealing enabled");
|
||||
|
||||
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
|
||||
// Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS
|
||||
let had_requests = sealing.last_request.map(|last_request|
|
||||
best_block.saturating_sub(last_request) <= SEALING_TIMEOUT_IN_BLOCKS
|
||||
@@ -655,8 +660,8 @@ impl Miner {
|
||||
|
||||
// TODO: (https://github.com/paritytech/parity-ethereum/issues/10407)
|
||||
// This is only used in authority_round path, and should be refactored to merge with the other seal() path.
|
||||
// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the
|
||||
// type of Seal.
|
||||
// Attempts to perform internal sealing (one that does not require work: e.g. Clique
|
||||
// and Aura) and handles the result depending on the type of Seal.
|
||||
fn seal_and_import_block_internally<C>(&self, chain: &C, block: ClosedBlock) -> bool
|
||||
where C: BlockChain + SealedBlockImporter,
|
||||
{
|
||||
@@ -669,63 +674,55 @@ impl Miner {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
trace!(target: "miner", "seal_block_internally: attempting internal seal.");
|
||||
let block_number = block.header.number();
|
||||
trace!(target: "miner", "seal_block_internally: attempting internal seal for block #{}", block_number);
|
||||
|
||||
let parent_header = match chain.block_header(BlockId::Hash(*block.header.parent_hash())) {
|
||||
Some(h) => {
|
||||
match h.decode() {
|
||||
Ok(decoded_hdr) => decoded_hdr,
|
||||
Err(_) => return false
|
||||
Err(e) => {
|
||||
error!(target: "miner", "seal_block_internally: Block #{}, Could not decode header from parent block (hash={}): {:?}", block_number, block.header.parent_hash(), e);
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
None => return false,
|
||||
},
|
||||
None => {
|
||||
trace!(target: "miner", "Block #{}: Parent with hash={} does not exist in our DB", block_number, block.header.parent_hash());
|
||||
return false
|
||||
},
|
||||
};
|
||||
|
||||
match self.engine.generate_seal(&block, &parent_header) {
|
||||
// Save proposal for later seal submission and broadcast it.
|
||||
Seal::Proposal(seal) => {
|
||||
trace!(target: "miner", "Received a Proposal seal.");
|
||||
{
|
||||
let mut sealing = self.sealing.lock();
|
||||
sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period;
|
||||
sealing.queue.set_pending(block.clone());
|
||||
sealing.queue.use_last_ref();
|
||||
}
|
||||
let sealing_result =
|
||||
match self.engine.generate_seal(&block, &parent_header) {
|
||||
// Directly import a regular sealed block.
|
||||
Seal::Regular(seal) => {
|
||||
trace!(target: "miner", "Block #{}: Received a Regular seal.", block_number);
|
||||
{
|
||||
let mut sealing = self.sealing.lock();
|
||||
sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period;
|
||||
}
|
||||
|
||||
block
|
||||
.lock()
|
||||
.seal(&*self.engine, seal)
|
||||
.map(|sealed| {
|
||||
chain.broadcast_proposal_block(sealed);
|
||||
true
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("ERROR: seal failed when given internally generated seal: {}", e);
|
||||
false
|
||||
})
|
||||
},
|
||||
// Directly import a regular sealed block.
|
||||
Seal::Regular(seal) => {
|
||||
trace!(target: "miner", "Received a Regular seal.");
|
||||
{
|
||||
let mut sealing = self.sealing.lock();
|
||||
sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period;
|
||||
}
|
||||
|
||||
block
|
||||
.lock()
|
||||
.seal(&*self.engine, seal)
|
||||
.map(|sealed| {
|
||||
chain.import_sealed_block(sealed).is_ok()
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("ERROR: seal failed when given internally generated seal: {}", e);
|
||||
false
|
||||
})
|
||||
},
|
||||
Seal::None => false,
|
||||
}
|
||||
block
|
||||
.lock()
|
||||
.seal(&*self.engine, seal)
|
||||
.map(|sealed| {
|
||||
match chain.import_sealed_block(sealed) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
error!(target: "miner", "Block #{}: seal_and_import_block_internally: import_sealed_block returned {:?}", block_number, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("ERROR: Block #{}, importing sealed block failed when given internally generated seal: {}", block_number, e);
|
||||
false
|
||||
})
|
||||
},
|
||||
Seal::None => false,
|
||||
};
|
||||
sealing_result
|
||||
}
|
||||
|
||||
/// Prepares work which has to be done to seal.
|
||||
@@ -793,28 +790,30 @@ impl Miner {
|
||||
}
|
||||
|
||||
/// Prepare a pending block. Returns the preparation status.
|
||||
fn prepare_pending_block<C>(&self, client: &C) -> BlockPreparationStatus where
|
||||
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
|
||||
/// Only used by externally sealing engines.
|
||||
fn prepare_pending_block<C>(&self, client: &C) -> BlockPreparationStatus
|
||||
where
|
||||
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
|
||||
{
|
||||
trace!(target: "miner", "prepare_pending_block: entering");
|
||||
let prepare_new = {
|
||||
let mut sealing = self.sealing.lock();
|
||||
let have_work = sealing.queue.peek_last_ref().is_some();
|
||||
trace!(target: "miner", "prepare_pending_block: have_work={}", have_work);
|
||||
if !have_work {
|
||||
sealing.enabled = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
// Unless we are `--force-sealing` we create pending blocks if
|
||||
// 1. we have local pending transactions
|
||||
// 2. or someone is requesting `eth_getWork`
|
||||
// When either condition is true, `sealing.enabled` is flipped to true (and if you
|
||||
// have no more local transactions or stop calling `eth_getWork`, it is set to `false`).
|
||||
|
||||
// Here we check if there are pending blocks already (if so, we don't need to create
|
||||
// a new one); if there are none, we set `sealing.enabled` to true because the
|
||||
// calling code expects it to be on (or they wouldn't have called this method).
|
||||
// Yes, it's a bit convoluted.
|
||||
let prepare_new_block = self.maybe_enable_sealing();
|
||||
|
||||
if self.engine.sealing_state() != SealingState::External {
|
||||
trace!(target: "miner", "prepare_pending_block: engine not sealing externally; not preparing");
|
||||
return BlockPreparationStatus::NotPrepared;
|
||||
}
|
||||
|
||||
let preparation_status = if prepare_new {
|
||||
let preparation_status = if prepare_new_block {
|
||||
// --------------------------------------------------------------------------
|
||||
// | NOTE Code below requires sealing locks. |
|
||||
// | Make sure to release the locks before calling that method. |
|
||||
@@ -844,25 +843,34 @@ impl Miner {
|
||||
preparation_status
|
||||
}
|
||||
|
||||
|
||||
/// Set `sealing.enabled` to true if there is available work to do (pending or in the queue).
|
||||
fn maybe_enable_sealing(&self) -> bool {
|
||||
let mut sealing = self.sealing.lock();
|
||||
if !sealing.work_available() {
|
||||
trace!(target: "miner", "maybe_enable_sealing: we have work to do so enabling sealing");
|
||||
sealing.enabled = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Prepare pending block, check whether sealing is needed, and then update sealing.
|
||||
fn prepare_and_update_sealing<C: miner::BlockChainClient>(&self, chain: &C) {
|
||||
use miner::MinerService;
|
||||
|
||||
// Make sure to do it after transaction is imported and lock is dropped.
|
||||
// We need to create pending block and enable sealing.
|
||||
let sealing_state = self.engine.sealing_state();
|
||||
if sealing_state == SealingState::Ready
|
||||
|| self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared {
|
||||
// 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.
|
||||
self.update_sealing(chain);
|
||||
match self.engine.sealing_state() {
|
||||
SealingState::Ready => self.update_sealing(chain),
|
||||
SealingState::External => {
|
||||
// this calls `maybe_enable_sealing()`
|
||||
if self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared {
|
||||
self.update_sealing(chain)
|
||||
}
|
||||
}
|
||||
SealingState::NotReady => { self.maybe_enable_sealing(); },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
|
||||
|
||||
impl miner::MinerService for Miner {
|
||||
type State = State<::state_db::StateDB>;
|
||||
|
||||
@@ -1720,12 +1728,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn internal_seals_without_work() {
|
||||
let _ = env_logger::try_init();
|
||||
let spec = Spec::new_instant();
|
||||
let miner = Miner::new_for_tests(&spec, None);
|
||||
|
||||
let client = generate_dummy_client(2);
|
||||
|
||||
let import = miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap();
|
||||
let import = miner.import_external_transactions(
|
||||
&*client,
|
||||
vec![transaction_with_chain_id(spec.chain_id()).into()]
|
||||
).pop().unwrap();
|
||||
assert_eq!(import.unwrap(), ());
|
||||
|
||||
miner.update_sealing(&*client);
|
||||
@@ -1733,7 +1745,10 @@ mod tests {
|
||||
assert!(miner.pending_block(0).is_none());
|
||||
assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber);
|
||||
|
||||
assert!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).is_ok());
|
||||
assert!(miner.import_own_transaction(
|
||||
&*client,
|
||||
PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)
|
||||
).is_ok());
|
||||
|
||||
miner.update_sealing(&*client);
|
||||
client.flush_queue();
|
||||
|
||||
Reference in New Issue
Block a user