2017-01-25 18:51:41 +01:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-09-08 12:12:24 +02:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2016-11-02 18:42:56 +01:00
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
2016-09-08 12:12:24 +02:00
2016-11-14 14:35:45 +01:00
use std ::sync ::atomic ::{ AtomicUsize , AtomicBool , Ordering as AtomicOrdering } ;
2017-07-29 21:56:42 +02:00
use std ::sync ::{ Weak , Arc } ;
2016-10-15 14:55:10 +02:00
use std ::time ::{ UNIX_EPOCH , Duration } ;
2017-09-26 14:19:08 +02:00
use std ::collections ::{ BTreeMap , HashSet } ;
2017-06-28 13:17:36 +02:00
2016-09-08 12:12:24 +02:00
use account_provider ::AccountProvider ;
use block ::* ;
2017-09-05 17:54:05 +02:00
use client ::EngineClient ;
2017-09-26 14:19:08 +02:00
use engines ::{ Engine , Seal , EngineError , ConstructedVerifier } ;
use error ::{ Error , BlockError } ;
2017-06-28 13:17:36 +02:00
use ethjson ;
2017-09-26 14:19:08 +02:00
use machine ::{ AuxiliaryData , Call , EthereumMachine } ;
2017-06-28 13:17:36 +02:00
use header ::{ Header , BlockNumber } ;
2017-01-20 13:25:17 +01:00
use super ::signer ::EngineSigner ;
2017-04-13 20:24:21 +02:00
use super ::validator_set ::{ ValidatorSet , SimpleList , new_validator_set } ;
2016-09-08 12:12:24 +02:00
2017-06-28 13:17:36 +02:00
use self ::finality ::RollingFinality ;
use ethkey ::{ verify_address , Signature } ;
use io ::{ IoContext , IoHandler , TimerToken , IoService } ;
use itertools ::{ self , Itertools } ;
use rlp ::{ UntrustedRlp , encode } ;
2017-09-04 16:36:49 +02:00
use bigint ::prelude ::{ U256 , U128 } ;
use bigint ::hash ::{ H256 , H520 } ;
2017-09-02 13:14:21 +02:00
use semantic_version ::SemanticVersion ;
2017-09-02 20:09:13 +02:00
use parking_lot ::{ Mutex , RwLock } ;
2017-09-05 12:14:03 +02:00
use unexpected ::{ Mismatch , OutOfBounds } ;
2017-06-28 13:17:36 +02:00
use util ::* ;
2017-09-06 20:47:45 +02:00
use bytes ::Bytes ;
2017-06-28 13:17:36 +02:00
mod finality ;
2016-09-08 12:12:24 +02:00
/// `AuthorityRound` params.
pub struct AuthorityRoundParams {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
/// Time to wait before next block or authority switching,
/// in seconds.
///
/// Deliberately typed as u16 as too high of a value leads
/// to slow block issuance.
pub step_duration : u16 ,
2016-12-06 19:23:15 +01:00
/// Starting step,
pub start_step : Option < u64 > ,
2017-01-10 12:23:59 +01:00
/// Valid validators.
2017-05-22 08:21:34 +02:00
pub validators : Box < ValidatorSet > ,
2017-03-28 10:46:52 +02:00
/// Chain score validation transition block.
pub validate_score_transition : u64 ,
2017-05-15 22:34:01 +02:00
/// Monotonic step validation transition block.
pub validate_step_transition : u64 ,
2017-06-28 13:17:36 +02:00
/// Immediate transitions.
pub immediate_transitions : bool ,
2017-09-26 14:19:08 +02:00
/// Block reward in base units.
pub block_reward : U256 ,
2017-12-05 15:57:45 +01:00
/// Number of accepted uncles transition block.
pub maximum_uncle_count_transition : u64 ,
2017-11-10 00:56:02 +01:00
/// Number of accepted uncles.
pub maximum_uncle_count : usize ,
2016-09-08 12:12:24 +02:00
}
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
const U16_MAX : usize = ::std ::u16 ::MAX as usize ;
2016-09-08 12:12:24 +02:00
impl From < ethjson ::spec ::AuthorityRoundParams > for AuthorityRoundParams {
fn from ( p : ethjson ::spec ::AuthorityRoundParams ) -> Self {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
let mut step_duration_usize : usize = p . step_duration . into ( ) ;
if step_duration_usize > U16_MAX {
step_duration_usize = U16_MAX ;
warn! ( target : " engine " , " step_duration is too high ({}), setting it to {} " , step_duration_usize , U16_MAX ) ;
}
2016-09-08 12:12:24 +02:00
AuthorityRoundParams {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
step_duration : step_duration_usize as u16 ,
2017-05-22 08:21:34 +02:00
validators : new_validator_set ( p . validators ) ,
2016-12-06 19:23:15 +01:00
start_step : p . start_step . map ( Into ::into ) ,
2017-03-28 10:46:52 +02:00
validate_score_transition : p . validate_score_transition . map_or ( 0 , Into ::into ) ,
2017-05-15 22:34:01 +02:00
validate_step_transition : p . validate_step_transition . map_or ( 0 , Into ::into ) ,
2017-06-28 13:17:36 +02:00
immediate_transitions : p . immediate_transitions . unwrap_or ( false ) ,
2017-09-26 14:19:08 +02:00
block_reward : p . block_reward . map_or_else ( Default ::default , Into ::into ) ,
2017-12-05 15:57:45 +01:00
maximum_uncle_count_transition : p . maximum_uncle_count_transition . map_or ( 0 , Into ::into ) ,
2017-11-10 00:56:02 +01:00
maximum_uncle_count : p . maximum_uncle_count . map_or ( 0 , Into ::into ) ,
2016-09-08 12:12:24 +02:00
}
}
}
2017-04-13 20:24:21 +02:00
// Helper for managing the step.
#[ derive(Debug) ]
struct Step {
calibrate : bool , // whether calibration is enabled.
inner : AtomicUsize ,
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
duration : u16 ,
2017-04-13 20:24:21 +02:00
}
impl Step {
fn load ( & self ) -> usize { self . inner . load ( AtomicOrdering ::SeqCst ) }
fn duration_remaining ( & self ) -> Duration {
let now = unix_now ( ) ;
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
let expected_seconds = ( self . load ( ) as u64 )
. checked_add ( 1 )
2017-12-21 15:01:05 +01:00
. and_then ( | ctr | ctr . checked_mul ( self . duration as u64 ) )
. map ( Duration ::from_secs ) ;
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
match expected_seconds {
2017-12-21 15:01:05 +01:00
Some ( step_end ) if step_end > now = > step_end - now ,
Some ( _ ) = > Duration ::from_secs ( 0 ) ,
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
None = > {
let ctr = self . load ( ) ;
error! ( target : " engine " , " Step counter is too high: {}, aborting " , ctr ) ;
panic! ( " step counter is too high: {} " , ctr )
} ,
2017-04-13 20:24:21 +02:00
}
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
2017-04-13 20:24:21 +02:00
}
2017-12-21 15:01:05 +01:00
2017-04-13 20:24:21 +02:00
fn increment ( & self ) {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
use std ::usize ;
// fetch_add won't panic on overflow but will rather wrap
// around, leading to zero as the step counter, which might
// lead to unexpected situations, so it's better to shut down.
if self . inner . fetch_add ( 1 , AtomicOrdering ::SeqCst ) = = usize ::MAX {
error! ( target : " engine " , " Step counter is too high: {}, aborting " , usize ::MAX ) ;
panic! ( " step counter is too high: {} " , usize ::MAX ) ;
}
2017-04-13 20:24:21 +02:00
}
2017-12-21 15:01:05 +01:00
2017-04-13 20:24:21 +02:00
fn calibrate ( & self ) {
if self . calibrate {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
let new_step = unix_now ( ) . as_secs ( ) / ( self . duration as u64 ) ;
2017-04-13 20:24:21 +02:00
self . inner . store ( new_step as usize , AtomicOrdering ::SeqCst ) ;
}
}
2017-12-21 15:01:05 +01:00
fn check_future ( & self , given : usize ) -> Result < ( ) , Option < OutOfBounds < u64 > > > {
const REJECTED_STEP_DRIFT : usize = 4 ;
2018-01-08 14:46:11 +01:00
// Verify if the step is correct.
if given < = self . load ( ) {
2017-12-21 15:01:05 +01:00
return Ok ( ( ) ) ;
}
// Make absolutely sure that the given step is incorrect.
self . calibrate ( ) ;
let current = self . load ( ) ;
// reject blocks too far in the future
if given > current + REJECTED_STEP_DRIFT {
Err ( None )
// wait a bit for blocks in near future
2018-01-08 14:46:11 +01:00
} else if given > current {
2017-12-21 15:01:05 +01:00
let d = self . duration as u64 ;
Err ( Some ( OutOfBounds {
min : None ,
2018-01-08 14:46:11 +01:00
max : Some ( d * current as u64 ) ,
2017-12-21 15:01:05 +01:00
found : d * given as u64 ,
} ) )
2017-04-13 20:24:21 +02:00
} else {
2017-12-21 15:01:05 +01:00
Ok ( ( ) )
2017-04-13 20:24:21 +02:00
}
}
}
2017-12-07 12:17:11 +01:00
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
fn calculate_score ( parent_step : U256 , current_step : U256 ) -> U256 {
U256 ::from ( U128 ::max_value ( ) ) + parent_step - current_step
}
2017-06-28 13:17:36 +02:00
struct EpochManager {
epoch_transition_hash : H256 ,
epoch_transition_number : BlockNumber ,
finality_checker : RollingFinality ,
force : bool ,
}
impl EpochManager {
fn blank ( ) -> Self {
EpochManager {
epoch_transition_hash : H256 ::default ( ) ,
epoch_transition_number : 0 ,
finality_checker : RollingFinality ::blank ( Vec ::new ( ) ) ,
force : true ,
}
}
// zoom to epoch for given header. returns true if succeeded, false otherwise.
2017-09-26 14:19:08 +02:00
fn zoom_to ( & mut self , client : & EngineClient , machine : & EthereumMachine , validators : & ValidatorSet , header : & Header ) -> bool {
2017-06-28 13:17:36 +02:00
let last_was_parent = self . finality_checker . subchain_head ( ) = = Some ( header . parent_hash ( ) . clone ( ) ) ;
// early exit for current target == chain head, but only if the epochs are
// the same.
if last_was_parent & & ! self . force {
return true ;
}
self . force = false ;
debug! ( target : " engine " , " Zooming to epoch for block {} " , header . hash ( ) ) ;
// epoch_transition_for can be an expensive call, but in the absence of
// forks it will only need to be called for the block directly after
// epoch transition, in which case it will be O(1) and require a single
// DB lookup.
let last_transition = match client . epoch_transition_for ( * header . parent_hash ( ) ) {
Some ( t ) = > t ,
None = > {
// this really should never happen unless the block passed
// hasn't got a parent in the database.
debug! ( target : " engine " , " No genesis transition found. " ) ;
return false ;
}
} ;
// extract other epoch set if it's not the same as the last.
if last_transition . block_hash ! = self . epoch_transition_hash {
let ( signal_number , set_proof , _ ) = destructure_proofs ( & last_transition . proof )
. expect ( " proof produced by this engine; therefore it is valid; qed " ) ;
trace! ( target : " engine " , " extracting epoch set for epoch ({}, {}) signalled at #{} " ,
last_transition . block_number , last_transition . block_hash , signal_number ) ;
let first = signal_number = = 0 ;
let epoch_set = validators . epoch_set (
first ,
2017-09-26 14:19:08 +02:00
machine ,
2017-06-28 13:17:36 +02:00
signal_number , // use signal number so multi-set first calculation is correct.
set_proof ,
)
. ok ( )
. map ( | ( list , _ ) | list . into_inner ( ) )
. expect ( " proof produced by this engine; therefore it is valid; qed " ) ;
self . finality_checker = RollingFinality ::blank ( epoch_set ) ;
}
self . epoch_transition_hash = last_transition . block_hash ;
self . epoch_transition_number = last_transition . block_number ;
true
}
// note new epoch hash. this will force the next block to re-load
// the epoch set
// TODO: optimize and don't require re-loading after epoch change.
fn note_new_epoch ( & mut self ) {
self . force = true ;
}
/// Get validator set. Zoom to the correct epoch first.
fn validators ( & self ) -> & SimpleList {
self . finality_checker . validators ( )
}
}
2017-04-13 20:24:21 +02:00
/// Engine using `AuthorityRound` proof-of-authority BFT consensus.
2016-09-08 12:12:24 +02:00
pub struct AuthorityRound {
2016-12-07 14:49:07 +01:00
transition_service : IoService < ( ) > ,
2017-04-13 20:24:21 +02:00
step : Arc < Step > ,
2017-07-13 09:48:00 +02:00
can_propose : AtomicBool ,
2017-01-10 12:23:59 +01:00
client : RwLock < Option < Weak < EngineClient > > > ,
2017-07-13 09:48:00 +02:00
signer : RwLock < EngineSigner > ,
2017-03-23 13:19:28 +01:00
validators : Box < ValidatorSet > ,
2017-03-28 10:46:52 +02:00
validate_score_transition : u64 ,
2017-05-15 22:34:01 +02:00
validate_step_transition : u64 ,
2017-06-28 13:17:36 +02:00
epoch_manager : Mutex < EpochManager > ,
immediate_transitions : bool ,
2017-09-26 14:19:08 +02:00
block_reward : U256 ,
2017-12-05 15:57:45 +01:00
maximum_uncle_count_transition : u64 ,
2017-11-10 00:56:02 +01:00
maximum_uncle_count : usize ,
2017-09-26 14:19:08 +02:00
machine : EthereumMachine ,
2016-10-15 14:55:10 +02:00
}
2017-04-13 20:24:21 +02:00
// header-chain validator.
2017-04-18 14:19:10 +02:00
struct EpochVerifier {
2017-04-13 20:24:21 +02:00
step : Arc < Step > ,
subchain_validators : SimpleList ,
}
2017-09-26 14:19:08 +02:00
impl super ::EpochVerifier < EthereumMachine > for EpochVerifier {
2017-04-13 20:24:21 +02:00
fn verify_light ( & self , header : & Header ) -> Result < ( ) , Error > {
// always check the seal since it's fast.
// nothing heavier to do.
2017-06-28 13:17:36 +02:00
verify_external ( header , & self . subchain_validators , & * self . step , | _ | { } )
}
fn check_finality_proof ( & self , proof : & [ u8 ] ) -> Option < Vec < H256 > > {
macro_rules ! otry {
( $e : expr ) = > {
match $e {
Some ( x ) = > x ,
None = > return None ,
}
}
}
let mut finality_checker = RollingFinality ::blank ( self . subchain_validators . clone ( ) . into_inner ( ) ) ;
let mut finalized = Vec ::new ( ) ;
let headers : Vec < Header > = otry! ( UntrustedRlp ::new ( proof ) . as_list ( ) . ok ( ) ) ;
for header in & headers {
// ensure all headers have correct number of seal fields so we can `verify_external`
// without panic.
//
// `verify_external` checks that signature is correct and author == signer.
if header . seal ( ) . len ( ) ! = 2 { return None }
otry! ( verify_external ( header , & self . subchain_validators , & * self . step , | _ | { } ) . ok ( ) ) ;
let newly_finalized = otry! ( finality_checker . push_hash ( header . hash ( ) , header . author ( ) . clone ( ) ) . ok ( ) ) ;
finalized . extend ( newly_finalized ) ;
}
if finalized . is_empty ( ) { None } else { Some ( finalized ) }
2017-04-13 20:24:21 +02:00
}
}
2017-06-28 13:17:36 +02:00
// Report misbehavior
#[ derive(Debug) ]
#[ allow(dead_code) ]
enum Report {
// Malicious behavior
Malicious ( Address , BlockNumber , Bytes ) ,
// benign misbehavior
Benign ( Address , BlockNumber ) ,
}
2016-11-14 18:42:56 +01:00
fn header_step ( header : & Header ) -> Result < usize , ::rlp ::DecoderError > {
2016-12-07 10:34:06 +01:00
UntrustedRlp ::new ( & header . seal ( ) . get ( 0 ) . expect ( " was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal) " ) ) . as_val ( )
2016-11-02 18:42:56 +01:00
}
2016-11-14 18:42:56 +01:00
fn header_signature ( header : & Header ) -> Result < Signature , ::rlp ::DecoderError > {
2016-12-07 10:34:06 +01:00
UntrustedRlp ::new ( & header . seal ( ) . get ( 1 ) . expect ( " was checked with verify_block_basic; has 2 fields; qed " ) ) . as_val ::< H520 > ( ) . map ( Into ::into )
2016-11-14 16:56:19 +01:00
}
2016-11-02 18:42:56 +01:00
2017-06-28 13:17:36 +02:00
fn step_proposer ( validators : & ValidatorSet , bh : & H256 , step : usize ) -> Address {
let proposer = validators . get ( bh , step ) ;
trace! ( target : " engine " , " Fetched proposer for step {}: {} " , step , proposer ) ;
proposer
}
fn is_step_proposer ( validators : & ValidatorSet , bh : & H256 , step : usize , address : & Address ) -> bool {
step_proposer ( validators , bh , step ) = = * address
}
fn verify_external < F : Fn ( Report ) > ( header : & Header , validators : & ValidatorSet , step : & Step , report : F )
-> Result < ( ) , Error >
{
2017-04-13 20:24:21 +02:00
let header_step = header_step ( header ) ? ;
2017-12-21 15:01:05 +01:00
match step . check_future ( header_step ) {
Err ( None ) = > {
trace! ( target : " engine " , " verify_block_external: block from the future " ) ;
report ( Report ::Benign ( * header . author ( ) , header . number ( ) ) ) ;
return Err ( BlockError ::InvalidSeal . into ( ) )
} ,
Err ( Some ( oob ) ) = > {
trace! ( target : " engine " , " verify_block_external: block too early " ) ;
return Err ( BlockError ::TemporarilyInvalid ( oob ) . into ( ) )
} ,
Ok ( _ ) = > {
let proposer_signature = header_signature ( header ) ? ;
let correct_proposer = validators . get ( header . parent_hash ( ) , header_step ) ;
let is_invalid_proposer = * header . author ( ) ! = correct_proposer | |
! verify_address ( & correct_proposer , & proposer_signature , & header . bare_hash ( ) ) ? ;
if is_invalid_proposer {
trace! ( target : " engine " , " verify_block_external: bad proposer for step: {} " , header_step ) ;
Err ( EngineError ::NotProposer ( Mismatch { expected : correct_proposer , found : header . author ( ) . clone ( ) } ) ) ?
} else {
Ok ( ( ) )
}
2017-04-13 20:24:21 +02:00
}
}
}
2017-06-28 13:17:36 +02:00
fn combine_proofs ( signal_number : BlockNumber , set_proof : & [ u8 ] , finality_proof : & [ u8 ] ) -> Vec < u8 > {
let mut stream = ::rlp ::RlpStream ::new_list ( 3 ) ;
stream . append ( & signal_number ) . append ( & set_proof ) . append ( & finality_proof ) ;
stream . out ( )
}
fn destructure_proofs ( combined : & [ u8 ] ) -> Result < ( BlockNumber , & [ u8 ] , & [ u8 ] ) , Error > {
let rlp = UntrustedRlp ::new ( combined ) ;
Ok ( (
rlp . at ( 0 ) ? . as_val ( ) ? ,
rlp . at ( 1 ) ? . data ( ) ? ,
rlp . at ( 2 ) ? . data ( ) ? ,
) )
}
2016-10-15 14:55:10 +02:00
trait AsMillis {
fn as_millis ( & self ) -> u64 ;
}
impl AsMillis for Duration {
fn as_millis ( & self ) -> u64 {
self . as_secs ( ) * 1_000 + ( self . subsec_nanos ( ) / 1_000_000 ) as u64
}
2016-09-08 12:12:24 +02:00
}
impl AuthorityRound {
2016-10-15 14:55:10 +02:00
/// Create a new instance of AuthorityRound engine.
2017-09-26 14:19:08 +02:00
pub fn new ( our_params : AuthorityRoundParams , machine : EthereumMachine ) -> Result < Arc < Self > , Error > {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
if our_params . step_duration = = 0 {
error! ( target : " engine " , " Authority Round step duration can't be zero, aborting " ) ;
panic! ( " authority_round: step duration can't be zero " )
}
2016-12-15 23:36:06 +01:00
let should_timeout = our_params . start_step . is_none ( ) ;
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
let initial_step = our_params . start_step . unwrap_or_else ( | | ( unix_now ( ) . as_secs ( ) / ( our_params . step_duration as u64 ) ) ) as usize ;
2016-09-08 12:12:24 +02:00
let engine = Arc ::new (
AuthorityRound {
2016-12-27 12:53:56 +01:00
transition_service : IoService ::< ( ) > ::start ( ) ? ,
2017-04-13 20:24:21 +02:00
step : Arc ::new ( Step {
inner : AtomicUsize ::new ( initial_step ) ,
calibrate : our_params . start_step . is_none ( ) ,
duration : our_params . step_duration ,
} ) ,
2017-07-13 09:48:00 +02:00
can_propose : AtomicBool ::new ( true ) ,
2017-01-10 12:23:59 +01:00
client : RwLock ::new ( None ) ,
2017-01-20 13:25:17 +01:00
signer : Default ::default ( ) ,
2017-05-22 08:21:34 +02:00
validators : our_params . validators ,
2017-03-28 10:46:52 +02:00
validate_score_transition : our_params . validate_score_transition ,
2017-05-15 22:34:01 +02:00
validate_step_transition : our_params . validate_step_transition ,
2017-06-28 13:17:36 +02:00
epoch_manager : Mutex ::new ( EpochManager ::blank ( ) ) ,
immediate_transitions : our_params . immediate_transitions ,
2017-09-26 14:19:08 +02:00
block_reward : our_params . block_reward ,
2017-12-05 15:57:45 +01:00
maximum_uncle_count_transition : our_params . maximum_uncle_count_transition ,
2017-11-10 00:56:02 +01:00
maximum_uncle_count : our_params . maximum_uncle_count ,
2017-09-26 14:19:08 +02:00
machine : machine ,
2016-09-08 12:12:24 +02:00
} ) ;
2017-06-28 13:17:36 +02:00
2016-12-15 23:36:06 +01:00
// Do not initialize timeouts for tests.
if should_timeout {
let handler = TransitionHandler { engine : Arc ::downgrade ( & engine ) } ;
2016-12-27 12:53:56 +01:00
engine . transition_service . register_handler ( Arc ::new ( handler ) ) ? ;
2016-12-15 23:36:06 +01:00
}
2016-11-14 18:42:56 +01:00
Ok ( engine )
2016-09-08 12:12:24 +02:00
}
}
2016-10-15 14:55:10 +02:00
fn unix_now ( ) -> Duration {
UNIX_EPOCH . elapsed ( ) . expect ( " Valid time has to be set in your system. " )
}
2016-09-08 12:12:24 +02:00
struct TransitionHandler {
engine : Weak < AuthorityRound > ,
}
2016-11-01 19:12:06 +01:00
const ENGINE_TIMEOUT_TOKEN : TimerToken = 23 ;
2016-09-08 12:12:24 +02:00
2016-12-07 14:49:07 +01:00
impl IoHandler < ( ) > for TransitionHandler {
fn initialize ( & self , io : & IoContext < ( ) > ) {
2016-09-08 12:12:24 +02:00
if let Some ( engine ) = self . engine . upgrade ( ) {
2017-04-13 20:24:21 +02:00
let remaining = engine . step . duration_remaining ( ) ;
io . register_timer_once ( ENGINE_TIMEOUT_TOKEN , remaining . as_millis ( ) )
2017-02-02 19:11:43 +01:00
. unwrap_or_else ( | e | warn! ( target : " engine " , " Failed to start consensus step timer: {}. " , e ) )
2016-09-08 12:12:24 +02:00
}
}
2016-12-07 14:49:07 +01:00
fn timeout ( & self , io : & IoContext < ( ) > , timer : TimerToken ) {
2016-09-08 12:12:24 +02:00
if timer = = ENGINE_TIMEOUT_TOKEN {
if let Some ( engine ) = self . engine . upgrade ( ) {
2018-01-08 14:46:11 +01:00
// NOTE we might be lagging by couple of steps in case the timeout
// has not been called fast enough.
// Make sure to advance up to the actual step.
while engine . step . duration_remaining ( ) . as_millis ( ) = = 0 {
engine . step ( ) ;
}
let next_run_at = engine . step . duration_remaining ( ) . as_millis ( ) > > 2 ;
io . register_timer_once ( ENGINE_TIMEOUT_TOKEN , next_run_at )
2017-02-02 19:11:43 +01:00
. unwrap_or_else ( | e | warn! ( target : " engine " , " Failed to restart consensus step timer: {}. " , e ) )
2016-09-08 12:12:24 +02:00
}
}
}
}
2017-09-26 14:19:08 +02:00
impl Engine < EthereumMachine > for AuthorityRound {
2016-09-08 12:12:24 +02:00
fn name ( & self ) -> & str { " AuthorityRound " }
2017-03-02 12:25:55 +01:00
2016-09-08 12:12:24 +02:00
fn version ( & self ) -> SemanticVersion { SemanticVersion ::new ( 1 , 0 , 0 ) }
2017-03-02 12:25:55 +01:00
2017-09-26 14:19:08 +02:00
fn machine ( & self ) -> & EthereumMachine { & self . machine }
2016-10-15 14:55:10 +02:00
/// Two fields - consensus step and the corresponding proposer signature.
2016-09-19 18:00:39 +02:00
fn seal_fields ( & self ) -> usize { 2 }
2016-09-08 12:12:24 +02:00
2016-12-06 19:23:15 +01:00
fn step ( & self ) {
2017-04-13 20:24:21 +02:00
self . step . increment ( ) ;
2017-07-13 09:48:00 +02:00
self . can_propose . store ( true , AtomicOrdering ::SeqCst ) ;
2017-01-10 12:23:59 +01:00
if let Some ( ref weak ) = * self . client . read ( ) {
if let Some ( c ) = weak . upgrade ( ) {
c . update_sealing ( ) ;
2016-12-06 19:23:15 +01:00
}
}
}
2016-09-08 12:12:24 +02:00
/// Additional engine-specific information for the user/developer concerning `header`.
2016-11-15 12:10:32 +01:00
fn extra_info ( & self , header : & Header ) -> BTreeMap < String , String > {
map! [
" step " . into ( ) = > header_step ( header ) . as_ref ( ) . map ( ToString ::to_string ) . unwrap_or ( " " . into ( ) ) ,
" signature " . into ( ) = > header_signature ( header ) . as_ref ( ) . map ( ToString ::to_string ) . unwrap_or ( " " . into ( ) )
]
}
2016-09-08 12:12:24 +02:00
2017-12-05 15:57:45 +01:00
fn maximum_uncle_count ( & self , block : BlockNumber ) -> usize {
if block > = self . maximum_uncle_count_transition {
self . maximum_uncle_count
} else {
// fallback to default value
2
}
}
2017-11-10 00:56:02 +01:00
2017-09-26 14:19:08 +02:00
fn populate_from_parent ( & self , header : & mut Header , parent : & Header ) {
2017-12-07 12:17:11 +01:00
let parent_step = header_step ( parent ) . expect ( " Header has been verified; qed " ) ;
let score = calculate_score ( parent_step . into ( ) , self . step . load ( ) . into ( ) ) ;
header . set_difficulty ( score ) ;
2016-09-08 12:12:24 +02:00
}
2017-02-20 16:35:53 +01:00
fn seals_internally ( & self ) -> Option < bool > {
2017-12-07 12:17:11 +01:00
// TODO: accept a `&Call` here so we can query the validator set.
2017-07-13 09:48:00 +02:00
Some ( self . signer . read ( ) . is_some ( ) )
2016-09-14 17:28:15 +02:00
}
2016-09-08 12:12:24 +02:00
/// Attempt to seal the block internally.
///
2017-07-26 17:25:32 +02:00
/// This operation is synchronous and may (quite reasonably) not be available, in which case
/// `Seal::None` will be returned.
2017-12-07 12:17:11 +01:00
fn generate_seal ( & self , block : & ExecutedBlock , parent : & Header ) -> Seal {
2017-06-22 20:44:04 +02:00
// first check to avoid generating signature most of the time
// (but there's still a race to the `compare_and_swap`)
2017-07-13 09:48:00 +02:00
if ! self . can_propose . load ( AtomicOrdering ::SeqCst ) { return Seal ::None ; }
2017-06-22 20:44:04 +02:00
2016-09-08 16:27:54 +02:00
let header = block . header ( ) ;
2017-12-07 12:17:11 +01:00
let parent_step : U256 = header_step ( parent )
. expect ( " Header has been verified; qed " ) . into ( ) ;
2017-04-13 20:24:21 +02:00
let step = self . step . load ( ) ;
2017-12-15 16:29:23 +01:00
2017-12-07 12:17:11 +01:00
let expected_diff = calculate_score ( parent_step , step . into ( ) ) ;
if header . difficulty ( ) ! = & expected_diff {
2018-01-08 14:46:11 +01:00
debug! ( target : " engine " , " Aborting seal generation. The step has changed in the meantime. {:?} != {:?} " ,
header . difficulty ( ) , expected_diff ) ;
return Seal ::None ;
}
if parent_step > step . into ( ) {
warn! ( target : " engine " , " Aborting seal generation for invalid step: {} > {} " , parent_step , step ) ;
2017-12-07 12:17:11 +01:00
return Seal ::None ;
}
2017-06-28 13:17:36 +02:00
// fetch correct validator set for current epoch, taking into account
// finality of previous transitions.
let active_set ;
let validators = if self . immediate_transitions {
& * self . validators
} else {
let mut epoch_manager = self . epoch_manager . lock ( ) ;
let client = match self . client . read ( ) . as_ref ( ) . and_then ( | weak | weak . upgrade ( ) ) {
Some ( client ) = > client ,
None = > {
warn! ( target : " engine " , " Unable to generate seal: missing client ref. " ) ;
return Seal ::None ;
}
} ;
2017-09-26 14:19:08 +02:00
if ! epoch_manager . zoom_to ( & * client , & self . machine , & * self . validators , header ) {
2017-06-28 13:17:36 +02:00
debug! ( target : " engine " , " Unable to zoom to epoch. " ) ;
return Seal ::None ;
}
active_set = epoch_manager . validators ( ) . clone ( ) ;
& active_set as & _
} ;
if is_step_proposer ( validators , header . parent_hash ( ) , step , header . author ( ) ) {
2017-12-15 16:29:23 +01:00
// 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 . into ( ) {
warn! ( " Attempted to seal block on the same step as parent. Is this authority sealing with more than one node? " ) ;
return Seal ::None ;
}
2017-07-13 09:48:00 +02:00
if let Ok ( signature ) = self . sign ( header . bare_hash ( ) ) {
2017-02-02 19:11:43 +01:00
trace! ( target : " engine " , " generate_seal: Issuing a block for step {}. " , step ) ;
2017-06-22 20:44:04 +02:00
// only issue the seal if we were the first to reach the compare_and_swap.
2017-07-13 09:48:00 +02:00
if self . can_propose . compare_and_swap ( true , false , AtomicOrdering ::SeqCst ) {
2017-06-28 14:16:53 +02:00
return Seal ::Regular ( vec! [ encode ( & step ) . into_vec ( ) , encode ( & ( & H520 ::from ( signature ) as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-06-22 20:44:04 +02:00
}
2016-09-08 12:12:24 +02:00
} else {
2017-02-02 19:11:43 +01:00
warn! ( target : " engine " , " generate_seal: FAIL: Accounts secret key unavailable. " ) ;
2016-09-08 12:12:24 +02:00
}
2016-12-06 19:23:15 +01:00
} else {
2017-06-28 13:17:36 +02:00
trace! ( target : " engine " , " generate_seal: {} not a proposer for step {}. " ,
header . author ( ) , step ) ;
2016-09-08 12:12:24 +02:00
}
2017-12-07 12:17:11 +01:00
2016-12-08 12:03:34 +01:00
Seal ::None
2016-09-08 12:12:24 +02:00
}
2017-09-26 14:19:08 +02:00
fn verify_local_seal ( & self , _header : & Header ) -> Result < ( ) , Error > {
Ok ( ( ) )
}
2017-06-28 13:17:36 +02:00
fn on_new_block (
& self ,
block : & mut ExecutedBlock ,
epoch_begin : bool ,
) -> Result < ( ) , Error > {
2017-08-29 15:16:24 +02:00
// with immediate transitions, we don't use the epoch mechanism anyway.
// the genesis is always considered an epoch, but we ignore it intentionally.
if self . immediate_transitions | | ! epoch_begin { return Ok ( ( ) ) }
2017-06-28 13:17:36 +02:00
// genesis is never a new block, but might as well check.
let header = block . fields ( ) . header . clone ( ) ;
let first = header . number ( ) = = 0 ;
let mut call = | to , data | {
2017-09-26 14:19:08 +02:00
let result = self . machine . execute_as_system (
2017-06-28 13:17:36 +02:00
block ,
to ,
U256 ::max_value ( ) , // unbounded gas? maybe make configurable.
Some ( data ) ,
) ;
result . map_err ( | e | format! ( " {} " , e ) )
} ;
self . validators . on_epoch_begin ( first , & header , & mut call )
}
2017-01-05 21:16:13 +01:00
/// Apply the block reward on finalisation of the block.
2017-05-30 11:52:33 +02:00
fn on_close_block ( & self , block : & mut ExecutedBlock ) -> Result < ( ) , Error > {
2017-09-26 14:19:08 +02:00
// TODO: move to "machine::WithBalances" trait.
::engines ::common ::bestow_block_reward ( block , self . block_reward )
2017-01-05 21:16:13 +01:00
}
2016-09-08 12:12:24 +02:00
/// Check the number of seal fields.
2017-12-07 12:17:11 +01:00
fn verify_block_basic ( & self , header : & Header ) -> Result < ( ) , Error > {
2017-09-26 14:19:08 +02:00
if header . number ( ) > = self . validate_score_transition & & * header . difficulty ( ) > = U256 ::from ( U128 ::max_value ( ) ) {
2017-03-28 10:46:52 +02:00
Err ( From ::from ( BlockError ::DifficultyOutOfBounds (
OutOfBounds { min : None , max : Some ( U256 ::from ( U128 ::max_value ( ) ) ) , found : * header . difficulty ( ) }
) ) )
2016-09-19 18:00:39 +02:00
} else {
Ok ( ( ) )
2016-09-08 12:12:24 +02:00
}
}
2017-04-13 20:24:21 +02:00
/// Do the step and gas limit validation.
2017-09-26 14:19:08 +02:00
fn verify_block_family ( & self , header : & Header , parent : & Header ) -> Result < ( ) , Error > {
2017-03-08 14:41:24 +01:00
let step = header_step ( header ) ? ;
2016-09-08 12:12:24 +02:00
2017-03-28 10:46:52 +02:00
let parent_step = header_step ( parent ) ? ;
2017-06-28 13:17:36 +02:00
2017-05-22 08:21:34 +02:00
// Ensure header is from the step after parent.
2017-05-15 22:34:01 +02:00
if step = = parent_step
| | ( header . number ( ) > = self . validate_step_transition & & step < = parent_step ) {
2017-04-13 20:24:21 +02:00
trace! ( target : " engine " , " Multiple blocks proposed for step {}. " , parent_step ) ;
2017-06-28 13:17:36 +02:00
self . validators . report_malicious ( header . author ( ) , header . number ( ) , header . number ( ) , Default ::default ( ) ) ;
2016-12-27 12:53:56 +01:00
Err ( EngineError ::DoubleVote ( header . author ( ) . clone ( ) ) ) ? ;
2016-11-01 19:12:06 +01:00
}
2017-09-26 14:19:08 +02:00
2017-05-22 08:21:34 +02:00
// Report skipped primaries.
2017-07-13 09:48:00 +02:00
if let ( true , Some ( me ) ) = ( step > parent_step + 1 , self . signer . read ( ) . address ( ) ) {
debug! ( target : " engine " , " Author {} built block with step gap. current step: {}, parent step: {} " ,
2017-06-28 13:17:36 +02:00
header . author ( ) , step , parent_step ) ;
2017-07-13 09:48:00 +02:00
let mut reported = HashSet ::new ( ) ;
2017-05-22 08:21:34 +02:00
for s in parent_step + 1 .. step {
2017-06-28 13:17:36 +02:00
let skipped_primary = step_proposer ( & * self . validators , & parent . hash ( ) , s ) ;
2017-07-13 09:48:00 +02:00
// Do not report this signer.
if skipped_primary ! = me {
self . validators . report_benign ( & skipped_primary , header . number ( ) , header . number ( ) ) ;
}
// Stop reporting once validators start repeating.
if ! reported . insert ( skipped_primary ) { break ; }
2017-05-22 08:21:34 +02:00
}
}
2016-11-01 19:12:06 +01:00
2016-09-08 12:12:24 +02:00
Ok ( ( ) )
}
2017-04-13 20:24:21 +02:00
// Check the validators.
2017-09-26 14:19:08 +02:00
fn verify_block_external ( & self , header : & Header ) -> Result < ( ) , Error > {
2017-06-28 13:17:36 +02:00
// fetch correct validator set for current epoch, taking into account
// finality of previous transitions.
let active_set ;
let ( validators , set_number ) = if self . immediate_transitions {
( & * self . validators , header . number ( ) )
} else {
// get correct validator set for epoch.
let client = match self . client . read ( ) . as_ref ( ) . and_then ( | weak | weak . upgrade ( ) ) {
Some ( client ) = > client ,
None = > {
debug! ( target : " engine " , " Unable to verify sig: missing client ref. " ) ;
return Err ( EngineError ::RequiresClient . into ( ) )
}
} ;
let mut epoch_manager = self . epoch_manager . lock ( ) ;
2017-09-26 14:19:08 +02:00
if ! epoch_manager . zoom_to ( & * client , & self . machine , & * self . validators , header ) {
2017-06-28 13:17:36 +02:00
debug! ( target : " engine " , " Unable to zoom to epoch. " ) ;
return Err ( EngineError ::RequiresClient . into ( ) )
}
active_set = epoch_manager . validators ( ) . clone ( ) ;
( & active_set as & _ , epoch_manager . epoch_transition_number )
} ;
2017-09-05 17:54:05 +02:00
// always report with "self.validators" so that the report actually gets
// to the contract.
2017-06-28 13:17:36 +02:00
let report = | report | match report {
Report ::Benign ( address , block_number ) = >
self . validators . report_benign ( & address , set_number , block_number ) ,
Report ::Malicious ( address , block_number , proof ) = >
self . validators . report_malicious ( & address , set_number , block_number , proof ) ,
} ;
// verify signature against fixed list, but reports should go to the
// contract itself.
verify_external ( header , validators , & * self . step , report )
2017-04-13 20:24:21 +02:00
}
2017-06-28 13:17:36 +02:00
fn genesis_epoch_data ( & self , header : & Header , call : & Call ) -> Result < Vec < u8 > , String > {
self . validators . genesis_epoch_data ( header , call )
. map ( | set_proof | combine_proofs ( 0 , & set_proof , & [ ] ) )
2017-04-12 18:55:38 +02:00
}
2017-09-26 14:19:08 +02:00
fn signals_epoch_end ( & self , header : & Header , aux : AuxiliaryData )
-> super ::EpochChange < EthereumMachine >
2017-04-12 18:55:38 +02:00
{
2017-06-28 13:17:36 +02:00
if self . immediate_transitions { return super ::EpochChange ::No }
let first = header . number ( ) = = 0 ;
2017-09-26 14:19:08 +02:00
self . validators . signals_epoch_end ( first , header , aux )
2017-04-12 18:55:38 +02:00
}
2017-06-28 13:17:36 +02:00
fn is_epoch_end (
& self ,
chain_head : & Header ,
2017-09-26 14:19:08 +02:00
chain : & super ::Headers < Header > ,
2017-06-28 13:17:36 +02:00
transition_store : & super ::PendingTransitionStore ,
) -> Option < Vec < u8 > > {
// epochs only matter if we want to support light clients.
if self . immediate_transitions { return None }
let first = chain_head . number ( ) = = 0 ;
2017-04-13 20:24:21 +02:00
2017-06-28 13:17:36 +02:00
// apply immediate transitions.
if let Some ( change ) = self . validators . is_epoch_end ( first , chain_head ) {
2017-08-04 21:43:31 +02:00
let change = combine_proofs ( chain_head . number ( ) , & change , & [ ] ) ;
2017-06-28 13:17:36 +02:00
return Some ( change )
}
let client = match self . client . read ( ) . as_ref ( ) . and_then ( | weak | weak . upgrade ( ) ) {
Some ( client ) = > client ,
None = > {
warn! ( target : " engine " , " Unable to check for epoch end: missing client ref. " ) ;
return None ;
}
} ;
// find most recently finalized blocks, then check transition store for pending transitions.
let mut epoch_manager = self . epoch_manager . lock ( ) ;
2017-09-26 14:19:08 +02:00
if ! epoch_manager . zoom_to ( & * client , & self . machine , & * self . validators , chain_head ) {
2017-06-28 13:17:36 +02:00
return None ;
}
if epoch_manager . finality_checker . subchain_head ( ) ! = Some ( * chain_head . parent_hash ( ) ) {
// build new finality checker from ancestry of chain head,
// not including chain head itself yet.
2017-07-13 09:48:00 +02:00
trace! ( target : " finality " , " Building finality up to parent of {} ({}) " ,
chain_head . hash ( ) , chain_head . parent_hash ( ) ) ;
2017-06-28 13:17:36 +02:00
let mut hash = chain_head . parent_hash ( ) . clone ( ) ;
let epoch_transition_hash = epoch_manager . epoch_transition_hash ;
// walk the chain within current epoch backwards.
// author == ec_recover(sig) known since
// the blocks are in the DB.
let ancestry_iter = itertools ::repeat_call ( move | | {
chain ( hash ) . and_then ( | header | {
if header . number ( ) = = 0 { return None }
let res = ( hash , header . author ( ) . clone ( ) ) ;
2017-07-13 09:48:00 +02:00
trace! ( target : " finality " , " Ancestry iteration: yielding {:?} " , res ) ;
2017-06-28 13:17:36 +02:00
hash = header . parent_hash ( ) . clone ( ) ;
Some ( res )
} )
} )
. while_some ( )
. take_while ( | & ( h , _ ) | h ! = epoch_transition_hash ) ;
if let Err ( _ ) = epoch_manager . finality_checker . build_ancestry_subchain ( ancestry_iter ) {
debug! ( target : " engine " , " inconsistent validator set within epoch " ) ;
return None ;
}
}
{
if let Ok ( finalized ) = epoch_manager . finality_checker . push_hash ( chain_head . hash ( ) , * chain_head . author ( ) ) {
let mut finalized = finalized . into_iter ( ) ;
2017-09-05 17:54:05 +02:00
while let Some ( finalized_hash ) = finalized . next ( ) {
if let Some ( pending ) = transition_store ( finalized_hash ) {
let finality_proof = ::std ::iter ::once ( finalized_hash )
2017-06-28 13:17:36 +02:00
. chain ( finalized )
. chain ( epoch_manager . finality_checker . unfinalized_hashes ( ) )
2017-09-05 17:54:05 +02:00
. map ( | h | if h = = chain_head . hash ( ) {
// chain closure only stores ancestry, but the chain head is also
// unfinalized.
chain_head . clone ( )
} else {
chain ( h ) . expect ( " these headers fetched before when constructing finality checker; qed " )
} )
2017-06-28 13:17:36 +02:00
. collect ::< Vec < Header > > ( ) ;
// this gives us the block number for `hash`, assuming it's ancestry.
let signal_number = chain_head . number ( )
- finality_proof . len ( ) as BlockNumber
+ 1 ;
let finality_proof = ::rlp ::encode_list ( & finality_proof ) ;
epoch_manager . note_new_epoch ( ) ;
2017-07-13 09:48:00 +02:00
info! ( target : " engine " , " Applying validator set change signalled at block {} " , signal_number ) ;
// We turn off can_propose here because upon validator set change there can
// be two valid proposers for a single step: one from the old set and
// one from the new.
//
// This way, upon encountering an epoch change, the proposer from the
// new set will be forced to wait until the next step to avoid sealing a
// block that breaks the invariant that the parent's step < the block's step.
self . can_propose . store ( false , AtomicOrdering ::SeqCst ) ;
2017-06-28 13:17:36 +02:00
return Some ( combine_proofs ( signal_number , & pending . proof , & * finality_proof ) ) ;
}
}
}
}
None
}
2017-09-26 14:19:08 +02:00
fn epoch_verifier < ' a > ( & self , _header : & Header , proof : & ' a [ u8 ] ) -> ConstructedVerifier < ' a , EthereumMachine > {
2017-06-28 13:17:36 +02:00
let ( signal_number , set_proof , finality_proof ) = match destructure_proofs ( proof ) {
Ok ( x ) = > x ,
Err ( e ) = > return ConstructedVerifier ::Err ( e ) ,
} ;
let first = signal_number = = 0 ;
2017-09-26 14:19:08 +02:00
match self . validators . epoch_set ( first , & self . machine , signal_number , set_proof ) {
2017-06-28 13:17:36 +02:00
Ok ( ( list , finalize ) ) = > {
let verifier = Box ::new ( EpochVerifier {
step : self . step . clone ( ) ,
subchain_validators : list ,
} ) ;
match finalize {
Some ( finalize ) = > ConstructedVerifier ::Unconfirmed ( verifier , finality_proof , finalize ) ,
None = > ConstructedVerifier ::Trusted ( verifier ) ,
}
}
Err ( e ) = > ConstructedVerifier ::Err ( e ) ,
}
2017-04-13 20:24:21 +02:00
}
2017-09-05 17:54:05 +02:00
fn register_client ( & self , client : Weak < EngineClient > ) {
2017-01-10 12:23:59 +01:00
* self . client . write ( ) = Some ( client . clone ( ) ) ;
2017-09-05 17:54:05 +02:00
self . validators . register_client ( client ) ;
2016-11-17 19:32:12 +01:00
}
2016-12-07 10:34:06 +01:00
2017-01-18 18:49:50 +01:00
fn set_signer ( & self , ap : Arc < AccountProvider > , address : Address , password : String ) {
2017-07-13 09:48:00 +02:00
self . signer . write ( ) . set ( ap , address , password ) ;
2016-09-27 12:12:18 +02:00
}
2017-01-24 10:03:58 +01:00
fn sign ( & self , hash : H256 ) -> Result < Signature , Error > {
2017-07-13 09:48:00 +02:00
self . signer . read ( ) . sign ( hash ) . map_err ( Into ::into )
2017-01-24 10:03:58 +01:00
}
2017-05-17 12:41:33 +02:00
fn snapshot_components ( & self ) -> Option < Box < ::snapshot ::SnapshotComponents > > {
2017-06-28 13:17:36 +02:00
if self . immediate_transitions {
None
} else {
Some ( Box ::new ( ::snapshot ::PoaSnapshot ) )
}
2017-05-17 12:41:33 +02:00
}
2016-09-08 12:12:24 +02:00
}
#[ cfg(test) ]
mod tests {
2017-07-29 21:56:42 +02:00
use std ::sync ::Arc ;
2017-05-22 08:21:34 +02:00
use std ::sync ::atomic ::{ AtomicUsize , Ordering as AtomicOrdering } ;
2017-08-30 19:18:28 +02:00
use hash ::keccak ;
2017-09-04 16:36:49 +02:00
use bigint ::prelude ::U256 ;
use bigint ::hash ::H520 ;
2016-11-14 12:09:57 +01:00
use header ::Header ;
2016-09-08 12:28:59 +02:00
use rlp ::encode ;
2016-09-08 12:12:24 +02:00
use block ::* ;
use tests ::helpers ::* ;
use account_provider ::AccountProvider ;
use spec ::Spec ;
2017-05-22 08:21:34 +02:00
use engines ::{ Seal , Engine } ;
use engines ::validator_set ::TestSet ;
use super ::{ AuthorityRoundParams , AuthorityRound } ;
2016-09-08 12:12:24 +02:00
#[ test ]
fn has_valid_metadata ( ) {
2016-09-14 11:20:22 +02:00
let engine = Spec ::new_test_round ( ) . engine ;
2016-09-08 12:12:24 +02:00
assert! ( ! engine . name ( ) . is_empty ( ) ) ;
assert! ( engine . version ( ) . major > = 1 ) ;
}
#[ test ]
fn can_return_schedule ( ) {
2016-09-14 11:20:22 +02:00
let engine = Spec ::new_test_round ( ) . engine ;
2017-04-19 14:30:00 +02:00
let schedule = engine . schedule ( 10000000 ) ;
2016-09-08 12:12:24 +02:00
assert! ( schedule . stack_limit > 0 ) ;
}
#[ test ]
fn can_do_signature_verification_fail ( ) {
2016-09-14 11:20:22 +02:00
let engine = Spec ::new_test_round ( ) . engine ;
2016-09-08 12:12:24 +02:00
let mut header : Header = Header ::default ( ) ;
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & H520 ::default ( ) ) . into_vec ( ) ] ) ;
2016-09-08 12:12:24 +02:00
2017-09-26 14:19:08 +02:00
let verify_result = engine . verify_block_external ( & header ) ;
2016-09-08 12:12:24 +02:00
assert! ( verify_result . is_err ( ) ) ;
}
#[ test ]
2016-11-14 15:15:31 +01:00
fn generates_seal_and_does_not_double_propose ( ) {
2017-01-18 18:49:50 +01:00
let tap = Arc ::new ( AccountProvider ::transient_provider ( ) ) ;
2017-08-30 19:18:28 +02:00
let addr1 = tap . insert_account ( keccak ( " 1 " ) . into ( ) , " 1 " ) . unwrap ( ) ;
let addr2 = tap . insert_account ( keccak ( " 2 " ) . into ( ) , " 2 " ) . unwrap ( ) ;
2016-09-08 12:12:24 +02:00
2016-09-14 11:20:22 +02:00
let spec = Spec ::new_test_round ( ) ;
2016-09-08 12:12:24 +02:00
let engine = & * spec . engine ;
let genesis_header = spec . genesis_header ( ) ;
2017-04-06 19:26:17 +02:00
let db1 = spec . ensure_db_good ( get_temp_state_db ( ) , & Default ::default ( ) ) . unwrap ( ) ;
let db2 = spec . ensure_db_good ( get_temp_state_db ( ) , & Default ::default ( ) ) . unwrap ( ) ;
2016-09-08 12:12:24 +02:00
let last_hashes = Arc ::new ( vec! [ genesis_header . hash ( ) ] ) ;
2017-06-28 13:17:36 +02:00
let b1 = OpenBlock ::new ( engine , Default ::default ( ) , false , db1 , & genesis_header , last_hashes . clone ( ) , addr1 , ( 3141562. into ( ) , 31415620. into ( ) ) , vec! [ ] , false ) . unwrap ( ) ;
2016-11-14 20:03:02 +01:00
let b1 = b1 . close_and_lock ( ) ;
2017-06-28 13:17:36 +02:00
let b2 = OpenBlock ::new ( engine , Default ::default ( ) , false , db2 , & genesis_header , last_hashes , addr2 , ( 3141562. into ( ) , 31415620. into ( ) ) , vec! [ ] , false ) . unwrap ( ) ;
2016-11-14 20:03:02 +01:00
let b2 = b2 . close_and_lock ( ) ;
2017-01-18 18:49:50 +01:00
engine . set_signer ( tap . clone ( ) , addr1 , " 1 " . into ( ) ) ;
2017-12-07 12:17:11 +01:00
if let Seal ::Regular ( seal ) = engine . generate_seal ( b1 . block ( ) , & genesis_header ) {
2017-04-12 14:41:19 +02:00
assert! ( b1 . clone ( ) . try_seal ( engine , seal ) . is_ok ( ) ) ;
2016-11-14 20:03:02 +01:00
// Second proposal is forbidden.
2017-12-07 12:17:11 +01:00
assert! ( engine . generate_seal ( b1 . block ( ) , & genesis_header ) = = Seal ::None ) ;
2016-11-14 20:03:02 +01:00
}
2017-01-18 18:49:50 +01:00
engine . set_signer ( tap , addr2 , " 2 " . into ( ) ) ;
2017-12-07 12:17:11 +01:00
if let Seal ::Regular ( seal ) = engine . generate_seal ( b2 . block ( ) , & genesis_header ) {
2017-04-12 14:41:19 +02:00
assert! ( b2 . clone ( ) . try_seal ( engine , seal ) . is_ok ( ) ) ;
2016-11-14 20:03:02 +01:00
// Second proposal is forbidden.
2017-12-07 12:17:11 +01:00
assert! ( engine . generate_seal ( b2 . block ( ) , & genesis_header ) = = Seal ::None ) ;
}
}
#[ test ]
fn checks_difficulty_in_generate_seal ( ) {
let tap = Arc ::new ( AccountProvider ::transient_provider ( ) ) ;
let addr1 = tap . insert_account ( keccak ( " 1 " ) . into ( ) , " 1 " ) . unwrap ( ) ;
let addr2 = tap . insert_account ( keccak ( " 0 " ) . into ( ) , " 0 " ) . unwrap ( ) ;
let spec = Spec ::new_test_round ( ) ;
let engine = & * spec . engine ;
let genesis_header = spec . genesis_header ( ) ;
let db1 = spec . ensure_db_good ( get_temp_state_db ( ) , & Default ::default ( ) ) . unwrap ( ) ;
let db2 = spec . ensure_db_good ( get_temp_state_db ( ) , & Default ::default ( ) ) . unwrap ( ) ;
let last_hashes = Arc ::new ( vec! [ genesis_header . hash ( ) ] ) ;
let b1 = OpenBlock ::new ( engine , Default ::default ( ) , false , db1 , & genesis_header , last_hashes . clone ( ) , addr1 , ( 3141562. into ( ) , 31415620. into ( ) ) , vec! [ ] , false ) . unwrap ( ) ;
let b1 = b1 . close_and_lock ( ) ;
let b2 = OpenBlock ::new ( engine , Default ::default ( ) , false , db2 , & genesis_header , last_hashes , addr2 , ( 3141562. into ( ) , 31415620. into ( ) ) , vec! [ ] , false ) . unwrap ( ) ;
let b2 = b2 . close_and_lock ( ) ;
engine . set_signer ( tap . clone ( ) , addr1 , " 1 " . into ( ) ) ;
match engine . generate_seal ( b1 . block ( ) , & genesis_header ) {
Seal ::None | Seal ::Proposal ( _ ) = > panic! ( " wrong seal " ) ,
Seal ::Regular ( _ ) = > {
engine . step ( ) ;
engine . set_signer ( tap . clone ( ) , addr2 , " 0 " . into ( ) ) ;
match engine . generate_seal ( b2 . block ( ) , & genesis_header ) {
Seal ::Regular ( _ ) | Seal ::Proposal ( _ ) = > panic! ( " sealed despite wrong difficulty " ) ,
Seal ::None = > { }
}
}
2016-11-14 20:03:02 +01:00
}
2016-09-08 12:12:24 +02:00
}
2016-09-08 16:27:54 +02:00
#[ test ]
fn proposer_switching ( ) {
let tap = AccountProvider ::transient_provider ( ) ;
2017-08-30 19:18:28 +02:00
let addr = tap . insert_account ( keccak ( " 0 " ) . into ( ) , " 0 " ) . unwrap ( ) ;
2017-03-08 14:41:24 +01:00
let mut parent_header : Header = Header ::default ( ) ;
2017-06-28 14:16:53 +02:00
parent_header . set_seal ( vec! [ encode ( & 0 usize ) . into_vec ( ) ] ) ;
2017-07-29 21:56:42 +02:00
parent_header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-03-08 14:41:24 +01:00
let mut header : Header = Header ::default ( ) ;
header . set_number ( 1 ) ;
2017-07-29 21:56:42 +02:00
header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2016-09-08 16:27:54 +02:00
header . set_author ( addr ) ;
2016-09-19 18:00:39 +02:00
let engine = Spec ::new_test_round ( ) . engine ;
2016-09-08 16:27:54 +02:00
2016-11-14 12:34:54 +01:00
let signature = tap . sign ( addr , Some ( " 0 " . into ( ) ) , header . bare_hash ( ) ) . unwrap ( ) ;
2017-01-10 12:23:59 +01:00
// Two validators.
2016-12-10 10:38:10 +01:00
// Spec starts with step 2.
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 2 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
assert! ( engine . verify_block_external ( & header ) . is_err ( ) ) ;
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 1 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
assert! ( engine . verify_block_external ( & header ) . is_ok ( ) ) ;
2016-09-08 16:27:54 +02:00
}
2016-12-10 10:38:10 +01:00
#[ test ]
fn rejects_future_block ( ) {
let tap = AccountProvider ::transient_provider ( ) ;
2017-08-30 19:18:28 +02:00
let addr = tap . insert_account ( keccak ( " 0 " ) . into ( ) , " 0 " ) . unwrap ( ) ;
2016-12-10 10:38:10 +01:00
2017-03-08 14:41:24 +01:00
let mut parent_header : Header = Header ::default ( ) ;
2017-06-28 14:16:53 +02:00
parent_header . set_seal ( vec! [ encode ( & 0 usize ) . into_vec ( ) ] ) ;
2017-07-29 21:56:42 +02:00
parent_header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-03-08 14:41:24 +01:00
let mut header : Header = Header ::default ( ) ;
header . set_number ( 1 ) ;
2017-07-29 21:56:42 +02:00
header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2016-12-10 10:38:10 +01:00
header . set_author ( addr ) ;
let engine = Spec ::new_test_round ( ) . engine ;
let signature = tap . sign ( addr , Some ( " 0 " . into ( ) ) , header . bare_hash ( ) ) . unwrap ( ) ;
2017-01-10 12:23:59 +01:00
// Two validators.
2016-12-10 10:38:10 +01:00
// Spec starts with step 2.
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 1 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
assert! ( engine . verify_block_external ( & header ) . is_ok ( ) ) ;
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 5 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
assert! ( engine . verify_block_external ( & header ) . is_err ( ) ) ;
2017-04-13 20:24:21 +02:00
}
#[ test ]
fn rejects_step_backwards ( ) {
2017-04-19 20:44:11 +02:00
let tap = AccountProvider ::transient_provider ( ) ;
2017-08-30 19:18:28 +02:00
let addr = tap . insert_account ( keccak ( " 0 " ) . into ( ) , " 0 " ) . unwrap ( ) ;
2017-04-13 20:24:21 +02:00
let mut parent_header : Header = Header ::default ( ) ;
2017-06-28 14:16:53 +02:00
parent_header . set_seal ( vec! [ encode ( & 4 usize ) . into_vec ( ) ] ) ;
2017-07-29 21:56:42 +02:00
parent_header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-04-13 20:24:21 +02:00
let mut header : Header = Header ::default ( ) ;
header . set_number ( 1 ) ;
2017-07-29 21:56:42 +02:00
header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-04-13 20:24:21 +02:00
header . set_author ( addr ) ;
let engine = Spec ::new_test_round ( ) . engine ;
let signature = tap . sign ( addr , Some ( " 0 " . into ( ) ) , header . bare_hash ( ) ) . unwrap ( ) ;
// Two validators.
// Spec starts with step 2.
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 5 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 3 usize ) . into_vec ( ) , encode ( & ( & * signature as & [ u8 ] ) ) . into_vec ( ) ] ) ;
2017-09-26 14:19:08 +02:00
assert! ( engine . verify_block_family ( & header , & parent_header ) . is_err ( ) ) ;
2016-12-10 10:38:10 +01:00
}
2017-05-22 08:21:34 +02:00
#[ test ]
fn reports_skipped ( ) {
let last_benign = Arc ::new ( AtomicUsize ::new ( 0 ) ) ;
let params = AuthorityRoundParams {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
step_duration : 1 ,
2017-05-22 08:21:34 +02:00
start_step : Some ( 1 ) ,
validators : Box ::new ( TestSet ::new ( Default ::default ( ) , last_benign . clone ( ) ) ) ,
validate_score_transition : 0 ,
validate_step_transition : 0 ,
2017-06-28 13:17:36 +02:00
immediate_transitions : true ,
2017-12-05 15:57:45 +01:00
maximum_uncle_count_transition : 0 ,
2017-11-10 00:56:02 +01:00
maximum_uncle_count : 0 ,
2017-09-26 14:19:08 +02:00
block_reward : Default ::default ( ) ,
2017-05-22 08:21:34 +02:00
} ;
2017-07-31 12:34:29 +02:00
let aura = {
let mut c_params = ::spec ::CommonParams ::default ( ) ;
c_params . gas_limit_bound_divisor = 5. into ( ) ;
2017-09-26 14:19:08 +02:00
let machine = ::machine ::EthereumMachine ::regular ( c_params , Default ::default ( ) ) ;
AuthorityRound ::new ( params , machine ) . unwrap ( )
2017-07-31 12:34:29 +02:00
} ;
2017-05-22 08:21:34 +02:00
let mut parent_header : Header = Header ::default ( ) ;
2017-06-28 14:16:53 +02:00
parent_header . set_seal ( vec! [ encode ( & 1 usize ) . into_vec ( ) ] ) ;
2017-07-29 21:56:42 +02:00
parent_header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-05-22 08:21:34 +02:00
let mut header : Header = Header ::default ( ) ;
header . set_number ( 1 ) ;
2017-07-29 21:56:42 +02:00
header . set_gas_limit ( " 222222 " . parse ::< U256 > ( ) . unwrap ( ) ) ;
2017-06-28 14:16:53 +02:00
header . set_seal ( vec! [ encode ( & 3 usize ) . into_vec ( ) ] ) ;
2017-05-22 08:21:34 +02:00
2017-07-13 09:48:00 +02:00
// Do not report when signer not present.
2017-09-26 14:19:08 +02:00
assert! ( aura . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
2017-07-13 09:48:00 +02:00
assert_eq! ( last_benign . load ( AtomicOrdering ::SeqCst ) , 0 ) ;
aura . set_signer ( Arc ::new ( AccountProvider ::transient_provider ( ) ) , Default ::default ( ) , Default ::default ( ) ) ;
2017-09-26 14:19:08 +02:00
assert! ( aura . verify_block_family ( & header , & parent_header ) . is_ok ( ) ) ;
2017-05-22 08:21:34 +02:00
assert_eq! ( last_benign . load ( AtomicOrdering ::SeqCst ) , 1 ) ;
}
2017-12-05 15:57:45 +01:00
#[ test ]
fn test_uncles_transition ( ) {
let last_benign = Arc ::new ( AtomicUsize ::new ( 0 ) ) ;
let params = AuthorityRoundParams {
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
step_duration : 1 ,
2017-12-05 15:57:45 +01:00
start_step : Some ( 1 ) ,
validators : Box ::new ( TestSet ::new ( Default ::default ( ) , last_benign . clone ( ) ) ) ,
validate_score_transition : 0 ,
validate_step_transition : 0 ,
immediate_transitions : true ,
maximum_uncle_count_transition : 1 ,
maximum_uncle_count : 0 ,
block_reward : Default ::default ( ) ,
} ;
let aura = {
let mut c_params = ::spec ::CommonParams ::default ( ) ;
c_params . gas_limit_bound_divisor = 5. into ( ) ;
let machine = ::machine ::EthereumMachine ::regular ( c_params , Default ::default ( ) ) ;
AuthorityRound ::new ( params , machine ) . unwrap ( )
} ;
assert_eq! ( aura . maximum_uncle_count ( 0 ) , 2 ) ;
assert_eq! ( aura . maximum_uncle_count ( 1 ) , 0 ) ;
assert_eq! ( aura . maximum_uncle_count ( 100 ) , 0 ) ;
}
Problem: AuRa's unsafeties around step duration (#7282)
Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.
Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`
Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)
Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.
As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).
2017-12-21 14:59:09 +01:00
#[ test ]
#[ should_panic(expected= " counter is too high " ) ]
fn test_counter_increment_too_high ( ) {
use super ::Step ;
let step = Step {
calibrate : false ,
inner : AtomicUsize ::new ( ::std ::usize ::MAX ) ,
duration : 1 ,
} ;
step . increment ( ) ;
}
#[ test ]
#[ should_panic(expected= " counter is too high " ) ]
fn test_counter_duration_remaining_too_high ( ) {
use super ::Step ;
let step = Step {
calibrate : false ,
inner : AtomicUsize ::new ( ::std ::usize ::MAX ) ,
duration : 1 ,
} ;
step . duration_remaining ( ) ;
}
#[ test ]
#[ should_panic(expected= " authority_round: step duration can't be zero " ) ]
fn test_step_duration_zero ( ) {
let last_benign = Arc ::new ( AtomicUsize ::new ( 0 ) ) ;
let params = AuthorityRoundParams {
step_duration : 0 ,
start_step : Some ( 1 ) ,
validators : Box ::new ( TestSet ::new ( Default ::default ( ) , last_benign . clone ( ) ) ) ,
validate_score_transition : 0 ,
validate_step_transition : 0 ,
immediate_transitions : true ,
maximum_uncle_count_transition : 0 ,
maximum_uncle_count : 0 ,
block_reward : Default ::default ( ) ,
} ;
let mut c_params = ::spec ::CommonParams ::default ( ) ;
c_params . gas_limit_bound_divisor = 5. into ( ) ;
let machine = ::machine ::EthereumMachine ::regular ( c_params , Default ::default ( ) ) ;
AuthorityRound ::new ( params , machine ) . unwrap ( ) ;
}
2016-09-08 12:12:24 +02:00
}