diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index abc324e68..d114bf173 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! A blockchain engine that supports a basic, non-BFT proof-of-authority. +//! A blockchain engine that supports a non-instant BFT proof-of-authority. use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::Weak; use std::time::{UNIX_EPOCH, Duration}; use util::*; -use ethkey::verify_address; -use rlp::{UntrustedRlp, View, encode, decode}; +use ethkey::{verify_address, Signature}; +use rlp::{UntrustedRlp, View, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; @@ -71,6 +71,17 @@ pub struct AuthorityRound { step: AtomicUsize } +impl Header { + fn step(&self) -> Result { + UntrustedRlp::new(&self.seal()[0]).as_val() + } + + fn signature(&self) -> Result { + UntrustedRlp::new(&self.seal()[1]).as_val::().map(Into::into) + } +} + + trait AsMillis { fn as_millis(&self) -> u64; } @@ -202,12 +213,13 @@ impl Engine for AuthorityRound { if let Some(ap) = accounts { // Account should be permanently unlocked, otherwise sealing will fail. if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) { + trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step); return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); } else { - trace!(target: "poa", "generate_seal: FAIL: accounts secret key unavailable"); + trace!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable."); } } else { - trace!(target: "poa", "generate_seal: FAIL: accounts not provided"); + trace!(target: "poa", "generate_seal: FAIL: Accounts not provided."); } } None @@ -227,10 +239,10 @@ impl Engine for AuthorityRound { /// Check if the signature belongs to the correct proposer. fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - let step = try!(UntrustedRlp::new(&header.seal()[0]).as_val::()); - if step <= self.step() { - let sig = try!(UntrustedRlp::new(&header.seal()[1]).as_val::()); - let ok_sig = try!(verify_address(self.step_proposer(step), &sig.into(), &header.bare_hash())); + let header_step = try!(header.step()); + // Give one step slack if step is lagging, double vote is still not possible. + if header_step <= self.step() + 1 { + let ok_sig = try!(verify_address(self.step_proposer(header_step), &try!(header.signature()), &header.bare_hash())); if ok_sig { Ok(()) } else { @@ -250,9 +262,7 @@ impl Engine for AuthorityRound { } // Check if parent is from a previous step. - let parent_step = try!(UntrustedRlp::new(&parent.seal()[0]).as_val::()); - let step = try!(UntrustedRlp::new(&header.seal()[0]).as_val::()); - if step <= parent_step { + if try!(header.step()) == try!(parent.step()) { try!(Err(BlockError::DoubleVote(header.author().clone()))); } @@ -284,13 +294,6 @@ impl Engine for AuthorityRound { } } -impl Header { - /// Get the none field of the header. - pub fn signature(&self) -> H520 { - decode(&self.seal()[0]) - } -} - #[cfg(test)] mod tests { use common::*;