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:
@@ -247,7 +247,7 @@ impl EpochManager {
|
||||
None => {
|
||||
// this really should never happen unless the block passed
|
||||
// hasn't got a parent in the database.
|
||||
warn!(target: "engine", "No genesis transition found.");
|
||||
warn!(target: "engine", "No genesis transition found. Block hash {} does not have a parent in the DB", hash);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -570,7 +570,7 @@ fn header_empty_steps_signers(header: &Header, empty_steps_transition: u64) -> R
|
||||
|
||||
fn step_proposer(validators: &dyn ValidatorSet, bh: &H256, step: u64) -> Address {
|
||||
let proposer = validators.get(bh, step as usize);
|
||||
trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer);
|
||||
trace!(target: "engine", "step_proposer: Fetched proposer for step {}: {}", step, proposer);
|
||||
proposer
|
||||
}
|
||||
|
||||
@@ -823,8 +823,8 @@ impl AuthorityRound {
|
||||
if skipped_primary != me {
|
||||
// Stop reporting once validators start repeating.
|
||||
if !reported.insert(skipped_primary) { break; }
|
||||
trace!(target: "engine", "Reporting benign misbehaviour (cause: skipped step) at block #{}, epoch set number {}. Own address: {}",
|
||||
header.number(), set_number, me);
|
||||
trace!(target: "engine", "Reporting benign misbehaviour (cause: skipped step) at block #{}, epoch set number {}, step proposer={:#x}. Own address: {}",
|
||||
header.number(), set_number, skipped_primary, me);
|
||||
self.validators.report_benign(&skipped_primary, set_number, header.number());
|
||||
} else {
|
||||
trace!(target: "engine", "Primary that skipped is self, not self-reporting. Own address: {}", me);
|
||||
@@ -1110,12 +1110,12 @@ impl Engine for AuthorityRound {
|
||||
|
||||
// filter messages from old and future steps and different parents
|
||||
let empty_steps = if header.number() >= self.empty_steps_transition {
|
||||
self.empty_steps(parent_step.into(), step.into(), *header.parent_hash())
|
||||
self.empty_steps(parent_step, step, *header.parent_hash())
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let expected_diff = calculate_score(parent_step, step.into(), empty_steps.len().into());
|
||||
let expected_diff = calculate_score(parent_step, step, empty_steps.len());
|
||||
|
||||
if header.difficulty() != &expected_diff {
|
||||
debug!(target: "engine", "Aborting seal generation. The step or empty_steps have changed in the meantime. {:?} != {:?}",
|
||||
@@ -1123,12 +1123,17 @@ impl Engine for AuthorityRound {
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
if parent_step > step.into() {
|
||||
if parent_step > step {
|
||||
warn!(target: "engine", "Aborting seal generation for invalid step: {} > {}", parent_step, step);
|
||||
return Seal::None;
|
||||
} else if parent_step == step {
|
||||
// this is guarded against by `can_propose` unless the block was signed
|
||||
// on the same step (implies same key) and on a different node.
|
||||
warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?");
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
let (validators, set_number) = match self.epoch_set(header) {
|
||||
let (validators, epoch_transition_number) = match self.epoch_set(header) {
|
||||
Err(err) => {
|
||||
warn!(target: "engine", "Unable to generate seal: {}", err);
|
||||
return Seal::None;
|
||||
@@ -1137,13 +1142,7 @@ impl Engine for AuthorityRound {
|
||||
};
|
||||
|
||||
if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) {
|
||||
// this is guarded against by `can_propose` unless the block was signed
|
||||
// on the same step (implies same key) and on a different node.
|
||||
if parent_step == step {
|
||||
warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?");
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
trace!(target: "engine", "generate_seal: we are step proposer for step={}, block=#{}", step, header.number());
|
||||
// if there are no transactions to include in the block, we don't seal and instead broadcast a signed
|
||||
// `EmptyStep(step, parent_hash)` message. If we exceed the maximum amount of `empty_step` rounds we proceed
|
||||
// with the seal.
|
||||
@@ -1152,6 +1151,7 @@ impl Engine for AuthorityRound {
|
||||
empty_steps.len() < self.maximum_empty_steps {
|
||||
|
||||
if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) {
|
||||
trace!(target: "engine", "generate_seal: generating empty step at step={}, block=#{}", step, header.number());
|
||||
self.generate_empty_step(header.parent_hash());
|
||||
}
|
||||
|
||||
@@ -1178,7 +1178,8 @@ impl Engine for AuthorityRound {
|
||||
// report any skipped primaries between the parent block and
|
||||
// the block we're sealing, unless we have empty steps enabled
|
||||
if header.number() < self.empty_steps_transition {
|
||||
self.report_skipped(header, step, parent_step, &*validators, set_number);
|
||||
trace!(target: "engine", "generate_seal: reporting misbehaviour for step={}, block=#{}", step, header.number());
|
||||
self.report_skipped(header, step, parent_step, &*validators, epoch_transition_number);
|
||||
}
|
||||
|
||||
let mut fields = vec![
|
||||
@@ -1189,7 +1190,7 @@ impl Engine for AuthorityRound {
|
||||
if let Some(empty_steps_rlp) = empty_steps_rlp {
|
||||
fields.push(empty_steps_rlp);
|
||||
}
|
||||
|
||||
trace!(target: "engine", "generate_seal: returning Seal::Regular for step={}, block=#{}", step, header.number());
|
||||
return Seal::Regular(fields);
|
||||
}
|
||||
} else {
|
||||
@@ -1199,7 +1200,7 @@ impl Engine for AuthorityRound {
|
||||
trace!(target: "engine", "generate_seal: {} not a proposer for step {}.",
|
||||
header.author(), step);
|
||||
}
|
||||
|
||||
trace!(target: "engine", "generate_seal: returning Seal::None for step={}, block=#{}", step, header.number());
|
||||
Seal::None
|
||||
}
|
||||
|
||||
@@ -1739,13 +1740,13 @@ mod tests {
|
||||
|
||||
engine.set_signer(Box::new((tap.clone(), addr1, "1".into())));
|
||||
match engine.generate_seal(&b1, &genesis_header) {
|
||||
Seal::None | Seal::Proposal(_) => panic!("wrong seal"),
|
||||
Seal::None => panic!("wrong seal"),
|
||||
Seal::Regular(_) => {
|
||||
engine.step();
|
||||
|
||||
engine.set_signer(Box::new((tap.clone(), addr2, "0".into())));
|
||||
match engine.generate_seal(&b2, &genesis_header) {
|
||||
Seal::Regular(_) | Seal::Proposal(_) => panic!("sealed despite wrong difficulty"),
|
||||
Seal::Regular(_) => panic!("sealed despite wrong difficulty"),
|
||||
Seal::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +156,6 @@ impl error::Error for EngineError {
|
||||
/// Seal type.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Seal {
|
||||
/// Proposal seal; should be broadcasted, but not inserted into blockchain.
|
||||
Proposal(Vec<Bytes>),
|
||||
/// Regular block seal; should be part of the blockchain.
|
||||
Regular(Vec<Bytes>),
|
||||
/// Engine does not generate seal for this block right now.
|
||||
|
||||
@@ -118,6 +118,7 @@ impl ValidatorSet for ValidatorContract {
|
||||
}
|
||||
|
||||
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
||||
trace!(target: "engine", "validator set recording benign misbehaviour at block #{} by {:#x}", block, address);
|
||||
let data = validator_report::functions::report_benign::encode_input(*address, block);
|
||||
match self.transact(data) {
|
||||
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
|
||||
@@ -162,6 +163,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn reports_validators() {
|
||||
let _ = ::env_logger::try_init();
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap();
|
||||
let client = generate_dummy_client_with_spec(Spec::new_validator_contract);
|
||||
|
||||
@@ -74,6 +74,8 @@ impl ::engines::StateDependentProof for StateProof {
|
||||
/// The validator contract should have the following interface:
|
||||
pub struct ValidatorSafeContract {
|
||||
contract_address: Address,
|
||||
/// The LRU cache is indexed by the parent_hash, so given a hash, the value
|
||||
/// is the validator set valid for the blocks following that hash.
|
||||
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
||||
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
|
||||
}
|
||||
@@ -471,6 +473,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn knows_validators() {
|
||||
let _ = env_logger::try_init();
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let s0: Secret = keccak("1").into();
|
||||
let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap();
|
||||
|
||||
@@ -93,6 +93,7 @@ impl ValidatorSet for TestSet {
|
||||
}
|
||||
|
||||
fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
||||
trace!(target: "engine", "test validator set recording benign misbehaviour");
|
||||
self.last_benign.store(block as usize, AtomicOrdering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user