add proposer step to seal

This commit is contained in:
keorn 2016-09-19 18:00:39 +02:00
parent 7f177f27d8
commit d84f94975f
2 changed files with 41 additions and 24 deletions

View File

@ -80,13 +80,17 @@ impl AuthorityRound {
engine
}
fn proposer(&self) -> &Address {
let ref p = self.our_params;
p.authorities.get(self.step.load(AtomicOrdering::Relaxed)%p.authority_n).unwrap()
fn step(&self) -> usize {
self.step.load(AtomicOrdering::SeqCst)
}
fn is_proposer(&self, address: &Address) -> bool {
self.proposer() == address
fn step_proposer(&self, step: usize) -> &Address {
let ref p = self.our_params;
p.authorities.get(step%p.authority_n).unwrap()
}
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
self.step_proposer(step) == address
}
}
@ -110,7 +114,7 @@ impl IoHandler<BlockArrived> for TransitionHandler {
if timer == ENGINE_TIMEOUT_TOKEN {
debug!(target: "authorityround", "timeout");
if let Some(engine) = self.engine.upgrade() {
engine.step.fetch_add(1, AtomicOrdering::Relaxed);
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.our_params.step_duration).expect("Failed to restart consensus step timer.")
}
}
@ -119,7 +123,7 @@ impl IoHandler<BlockArrived> for TransitionHandler {
fn message(&self, io: &IoContext<BlockArrived>, _net_message: &BlockArrived) {
if let Some(engine) = self.engine.upgrade() {
trace!(target: "authorityround", "Message: {:?}", get_time().sec);
engine.step.fetch_add(1, AtomicOrdering::Relaxed);
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
io.clear_timer(ENGINE_TIMEOUT_TOKEN).expect("Failed to restart consensus step timer.");
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.our_params.step_duration).expect("Failed to restart consensus step timer.")
}
@ -130,7 +134,7 @@ impl Engine for AuthorityRound {
fn name(&self) -> &str { "AuthorityRound" }
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
// One field - the proposer signature.
fn seal_fields(&self) -> usize { 1 }
fn seal_fields(&self) -> usize { 2 }
fn params(&self) -> &CommonParams { &self.params }
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
@ -170,11 +174,12 @@ impl Engine for AuthorityRound {
/// be returned.
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
let header = block.header();
if self.is_proposer(header.author()) {
let step = self.step();
if self.is_step_proposer(step, header.author()) {
if let Some(ap) = accounts {
// Account should be permanently unlocked, otherwise sealing will fail.
if let Ok(signature) = ap.sign(*header.author(), header.bare_hash()) {
return Some(vec![encode(&(&*signature as &[u8])).to_vec()]);
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
} else {
trace!(target: "authorityround", "generate_seal: FAIL: accounts secret key unavailable");
}
@ -188,19 +193,30 @@ impl Engine for AuthorityRound {
/// Check the number of seal fields.
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
if header.seal().len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
trace!(target: "authorityround", "verify_block_basic: wrong number of seal fields");
Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
)));
}
)))
} else {
Ok(())
}
}
/// Check if the signature belongs to the correct proposer.
/// TODO: Keep track of BlockNumber to step relationship
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>());
if try!(verify_address(self.proposer(), &sig.into(), &header.bare_hash())) {
let step = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<usize>());
if step <= self.step() {
let sig = try!(UntrustedRlp::new(&header.seal()[1]).as_val::<H520>());
let ok_sig = try!(verify_address(self.step_proposer(step), &sig.into(), &header.bare_hash()));
if ok_sig {
Ok(())
} else {
trace!(target: "authorityround", "verify_block_unordered: invalid seal signature");
try!(Err(BlockError::InvalidSeal))
}
} else {
trace!(target: "authorityround", "verify_block_unordered: block from the future");
try!(Err(BlockError::InvalidSeal))
}
}
@ -320,7 +336,6 @@ mod tests {
#[test]
fn proposer_switching() {
let engine = Spec::new_test_round().engine;
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("0".sha3(), "0").unwrap();
@ -328,19 +343,21 @@ mod tests {
header.set_author(addr);
let signature = tap.sign_with_password(addr, "0".into(), header.bare_hash()).unwrap();
header.set_seal(vec![encode(&(&*signature as &[u8])).to_vec()]);
header.set_seal(vec![vec![1u8], encode(&(&*signature as &[u8])).to_vec()]);
// Wrong step.
assert!(engine.verify_block_unordered(&header, None).is_err());
let engine = Spec::new_test_round().engine;
// Too early.
assert!(engine.verify_block_seal(&header).is_err());
sleep(Duration::from_millis(1000));
// Right step.
assert!(engine.verify_block_unordered(&header, None).is_ok());
assert!(engine.verify_block_seal(&header).is_ok());
sleep(Duration::from_millis(1000));
// Wrong step.
assert!(engine.verify_block_unordered(&header, None).is_err());
// Future step.
assert!(engine.verify_block_seal(&header).is_ok());
}
}

View File

@ -22,7 +22,7 @@ use std::time::Duration;
use ethcore::client::BlockChainClient;
#[test]
fn issue_tx() {
fn 2_peer_1_tx_seal() {
::env_logger::init().ok();
let mut net = MockNet::new_with_spec(2, vec!["1".sha3()], &Spec::new_test_round);
net.peer(1).issue_rand_tx();