diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 1000cfda2..fed69aaf0 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -83,6 +83,8 @@ pub struct AuthorityRound { client: RwLock>>, signer: EngineSigner, validators: Box, + /// Is this Engine just for testing (prevents step calibration). + calibrate_step: bool, } fn header_step(header: &Header) -> Result { @@ -122,6 +124,7 @@ impl AuthorityRound { client: RwLock::new(None), signer: Default::default(), validators: new_validator_set(our_params.validators), + calibrate_step: our_params.start_step.is_none(), }); // Do not initialize timeouts for tests. if should_timeout { @@ -131,6 +134,12 @@ impl AuthorityRound { Ok(engine) } + fn calibrate_step(&self) { + if self.calibrate_step { + self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst); + } + } + fn remaining_step_duration(&self) -> Duration { let now = unix_now(); let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1); @@ -148,6 +157,16 @@ impl AuthorityRound { fn is_step_proposer(&self, step: usize, address: &Address) -> bool { self.step_proposer(step) == *address } + + fn is_future_step(&self, step: usize) -> bool { + if step > self.step.load(AtomicOrdering::SeqCst) + 1 { + // Make absolutely sure that the step is correct. + self.calibrate_step(); + step > self.step.load(AtomicOrdering::SeqCst) + 1 + } else { + false + } + } } fn unix_now() -> Duration { @@ -285,7 +304,11 @@ impl Engine for AuthorityRound { fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let header_step = header_step(header)?; // Give one step slack if step is lagging, double vote is still not possible. - if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 { + if self.is_future_step(header_step) { + trace!(target: "engine", "verify_block_unordered: block from the future"); + self.validators.report_benign(header.author()); + Err(BlockError::InvalidSeal)? + } else { let proposer_signature = header_signature(header)?; let correct_proposer = self.step_proposer(header_step); if verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? { @@ -294,10 +317,6 @@ impl Engine for AuthorityRound { trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step); Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))? } - } else { - trace!(target: "engine", "verify_block_unordered: block from the future"); - self.validators.report_benign(header.author()); - Err(BlockError::InvalidSeal)? } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 2320f49a0..2cc1ff21f 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -166,7 +166,9 @@ pub trait Engine : Sync + Send { } /// The network ID that transactions should be signed with. - fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { None } + fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { + Some(self.params().chain_id) + } /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer