add tests, fixes, simplifications

This commit is contained in:
keorn 2016-09-08 16:27:54 +02:00
parent 747898d8e7
commit fc3d01ec71
4 changed files with 50 additions and 20 deletions

View File

@ -1,11 +1,14 @@
{ {
"name": "TestAuthorityRound", "name": "TestAuthorityRound",
"engine": { "engine": {
"BasicAuthority": { "AuthorityRound": {
"params": { "params": {
"gasLimitBoundDivisor": "0x0400", "gasLimitBoundDivisor": "0x0400",
"stepDuration": "0x0d", "stepDuration": "0x03e8",
"authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"] "authorities" : [
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
]
} }
} }
}, },

View File

@ -19,7 +19,7 @@
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::sync::Weak; use std::sync::Weak;
use common::*; use common::*;
use ethkey::{recover, public_to_address}; use ethkey::verify_address;
use rlp::{UntrustedRlp, View, encode, decode}; use rlp::{UntrustedRlp, View, encode, decode};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::*; use block::*;
@ -108,6 +108,7 @@ impl IoHandler<BlockArrived> for TransitionHandler {
fn timeout(&self, io: &IoContext<BlockArrived>, timer: TimerToken) { fn timeout(&self, io: &IoContext<BlockArrived>, timer: TimerToken) {
if timer == ENGINE_TIMEOUT_TOKEN { if timer == ENGINE_TIMEOUT_TOKEN {
println!("timeout");
if let Some(engine) = self.engine.upgrade() { if let Some(engine) = self.engine.upgrade() {
engine.step.fetch_add(1, AtomicOrdering::Relaxed); engine.step.fetch_add(1, AtomicOrdering::Relaxed);
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.our_params.step_duration).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.")
@ -163,12 +164,11 @@ impl Engine for AuthorityRound {
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned. /// be returned.
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
if self.is_proposer(block.header().author()) { let header = block.header();
if self.is_proposer(header.author()) {
if let Some(ap) = accounts { if let Some(ap) = accounts {
let header = block.header(); // Account should be permanently unlocked, otherwise sealing will fail.
let message = header.bare_hash(); if let Ok(signature) = ap.sign(*header.author(), header.bare_hash()) {
// Account should be pernamently unlocked, otherwise sealing will fail.
if let Ok(signature) = ap.sign(*header.author(), message) {
return Some(vec![encode(&(&*signature as &[u8])).to_vec()]); return Some(vec![encode(&(&*signature as &[u8])).to_vec()]);
} else { } else {
trace!(target: "authorityround", "generate_seal: FAIL: accounts secret key unavailable"); trace!(target: "authorityround", "generate_seal: FAIL: accounts secret key unavailable");
@ -181,7 +181,7 @@ impl Engine for AuthorityRound {
} }
/// Check the number of seal fields. /// Check the number of seal fields.
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
if header.seal().len() != self.seal_fields() { if header.seal().len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity( return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal().len() } Mismatch { expected: self.seal_fields(), found: header.seal().len() }
@ -191,17 +191,16 @@ impl Engine for AuthorityRound {
} }
/// Check if the signature belongs to the correct proposer. /// Check if the signature belongs to the correct proposer.
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()); let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>());
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash()))); if try!(verify_address(self.proposer(), &sig.into(), &header.bare_hash())) {
if self.is_proposer(&signer) {
Ok(()) Ok(())
} else { } else {
try!(Err(BlockError::InvalidSeal)) try!(Err(BlockError::InvalidSeal))
} }
} }
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
// Don't calculate difficulty for genesis blocks. // Don't calculate difficulty for genesis blocks.
if header.number() == 0 { if header.number() == 0 {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
@ -220,7 +219,7 @@ impl Engine for AuthorityRound {
Ok(()) Ok(())
} }
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> result::Result<(), Error> { fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
try!(t.check_low_s()); try!(t.check_low_s());
Ok(()) Ok(())
} }
@ -245,6 +244,8 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use spec::Spec; use spec::Spec;
use std::thread::sleep;
use std::time::Duration;
/// Create a new test chain spec with `AuthorityRound` consensus engine. /// Create a new test chain spec with `AuthorityRound` consensus engine.
fn new_test_authority() -> Spec { fn new_test_authority() -> Spec {
@ -276,7 +277,7 @@ mod tests {
} }
#[test] #[test]
fn can_do_seal_verification_fail() { fn verification_fails_on_short_seal() {
let engine = new_test_authority().engine; let engine = new_test_authority().engine;
let header: Header = Header::default(); let header: Header = Header::default();
@ -302,8 +303,8 @@ mod tests {
#[test] #[test]
fn can_generate_seal() { fn can_generate_seal() {
let tap = AccountProvider::transient_provider(); let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap(); let addr = tap.insert_account("1".sha3(), "1").unwrap();
tap.unlock_account_permanently(addr, "".into()).unwrap(); tap.unlock_account_permanently(addr, "1".into()).unwrap();
let spec = new_test_authority(); let spec = new_test_authority();
let engine = &*spec.engine; let engine = &*spec.engine;
@ -317,4 +318,30 @@ mod tests {
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine, seal).is_ok()); assert!(b.try_seal(engine, seal).is_ok());
} }
#[test]
fn proposer_switching() {
let engine = new_test_authority().engine;
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("0".sha3(), "0").unwrap();
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()]);
// Wrong step.
assert!(engine.verify_block_unordered(&header, None).is_err());
sleep(Duration::from_millis(1000));
// Right step.
assert!(engine.verify_block_unordered(&header, None).is_ok());
sleep(Duration::from_millis(1000));
// Wrong step.
assert!(engine.verify_block_unordered(&header, None).is_err());
}
} }

View File

@ -181,7 +181,7 @@ mod tests {
/// Create a new test chain spec with `BasicAuthority` consensus engine. /// Create a new test chain spec with `BasicAuthority` consensus engine.
fn new_test_authority() -> Spec { fn new_test_authority() -> Spec {
let bytes: &[u8] = include_bytes!("../../res/test_authority.json"); let bytes: &[u8] = include_bytes!("../../res/basic_authority.json");
Spec::load(bytes).expect("invalid chain spec") Spec::load(bytes).expect("invalid chain spec")
} }

View File

@ -42,7 +42,7 @@ pub struct AuthorityRound {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serde_json; use serde_json;
use spec::basic_authority::AuthorityRound; use spec::authority_round::AuthorityRound;
#[test] #[test]
fn basic_authority_deserialization() { fn basic_authority_deserialization() {