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:
David
2019-07-04 18:03:22 +02:00
committed by GitHub
parent fafb534cd3
commit de906d4afd
13 changed files with 153 additions and 120 deletions

View File

@@ -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();