16
16
17
17
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
18
18
19
+ use std:: collections:: { BTreeMap , HashSet } ;
19
20
use std:: fmt;
21
+ use std:: iter:: FromIterator ;
22
+ use std:: ops:: Deref ;
20
23
use std:: sync:: atomic:: { AtomicUsize , AtomicBool , Ordering as AtomicOrdering } ;
21
24
use std:: sync:: { Weak , Arc } ;
22
25
use std:: time:: { UNIX_EPOCH , SystemTime , Duration } ;
23
- use std:: collections:: { BTreeMap , HashSet } ;
24
- use std:: iter:: FromIterator ;
25
26
26
27
use account_provider:: AccountProvider ;
27
28
use block:: * ;
28
29
use client:: EngineClient ;
29
30
use engines:: { Engine , Seal , EngineError , ConstructedVerifier } ;
30
31
use engines:: block_reward;
31
32
use engines:: block_reward:: { BlockRewardContract , RewardKind } ;
32
- use error:: { Error , BlockError } ;
33
+ use error:: { Error , ErrorKind , BlockError } ;
33
34
use ethjson;
34
35
use machine:: { AuxiliaryData , Call , EthereumMachine } ;
35
36
use hash:: keccak;
@@ -572,7 +573,6 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans
572
573
573
574
if is_invalid_proposer {
574
575
trace ! ( target: "engine" , "verify_block_external: bad proposer for step: {}" , header_step) ;
575
- validators. report_benign ( header. author ( ) , header. number ( ) , header. number ( ) ) ;
576
576
Err ( EngineError :: NotProposer ( Mismatch { expected : correct_proposer, found : header. author ( ) . clone ( ) } ) ) ?
577
577
} else {
578
578
Ok ( ( ) )
@@ -604,6 +604,23 @@ impl AsMillis for Duration {
604
604
}
605
605
}
606
606
607
+ // A type for storing owned or borrowed data that has a common type.
608
+ // Useful for returning either a borrow or owned data from a function.
609
+ enum CowLike < ' a , A : ' a + ?Sized , B > {
610
+ Borrowed ( & ' a A ) ,
611
+ Owned ( B ) ,
612
+ }
613
+
614
+ impl < ' a , A : ?Sized , B > Deref for CowLike < ' a , A , B > where B : AsRef < A > {
615
+ type Target = A ;
616
+ fn deref ( & self ) -> & A {
617
+ match self {
618
+ CowLike :: Borrowed ( b) => b,
619
+ CowLike :: Owned ( o) => o. as_ref ( ) ,
620
+ }
621
+ }
622
+ }
623
+
607
624
impl AuthorityRound {
608
625
/// Create a new instance of AuthorityRound engine.
609
626
pub fn new ( our_params : AuthorityRoundParams , machine : EthereumMachine ) -> Result < Arc < Self > , Error > {
@@ -653,6 +670,30 @@ impl AuthorityRound {
653
670
Ok ( engine)
654
671
}
655
672
673
+ // fetch correct validator set for epoch at header, taking into account
674
+ // finality of previous transitions.
675
+ fn epoch_set < ' a > ( & ' a self , header : & Header ) -> Result < ( CowLike < ValidatorSet , SimpleList > , BlockNumber ) , Error > {
676
+ Ok ( if self . immediate_transitions {
677
+ ( CowLike :: Borrowed ( & * self . validators ) , header. number ( ) )
678
+ } else {
679
+ let mut epoch_manager = self . epoch_manager . lock ( ) ;
680
+ let client = match self . client . read ( ) . as_ref ( ) . and_then ( |weak| weak. upgrade ( ) ) {
681
+ Some ( client) => client,
682
+ None => {
683
+ debug ! ( target: "engine" , "Unable to verify sig: missing client ref." ) ;
684
+ return Err ( EngineError :: RequiresClient . into ( ) )
685
+ }
686
+ } ;
687
+
688
+ if !epoch_manager. zoom_to ( & * client, & self . machine , & * self . validators , header) {
689
+ debug ! ( target: "engine" , "Unable to zoom to epoch." ) ;
690
+ return Err ( EngineError :: RequiresClient . into ( ) )
691
+ }
692
+
693
+ ( CowLike :: Owned ( epoch_manager. validators ( ) . clone ( ) ) , epoch_manager. epoch_transition_number )
694
+ } )
695
+ }
696
+
656
697
fn empty_steps ( & self , from_step : U256 , to_step : U256 , parent_hash : H256 ) -> Vec < EmptyStep > {
657
698
self . empty_steps . lock ( ) . iter ( ) . filter ( |e| {
658
699
U256 :: from ( e. step ) > from_step &&
@@ -697,6 +738,28 @@ impl AuthorityRound {
697
738
}
698
739
}
699
740
}
741
+
742
+ fn report_skipped ( & self , header : & Header , current_step : usize , parent_step : usize , validators : & ValidatorSet , set_number : u64 ) {
743
+ // we're building on top of the genesis block so don't report any skipped steps
744
+ if header. number ( ) == 1 {
745
+ return ;
746
+ }
747
+
748
+ if let ( true , Some ( me) ) = ( current_step > parent_step + 1 , self . signer . read ( ) . address ( ) ) {
749
+ debug ! ( target: "engine" , "Author {} built block with step gap. current step: {}, parent step: {}" ,
750
+ header. author( ) , current_step, parent_step) ;
751
+ let mut reported = HashSet :: new ( ) ;
752
+ for step in parent_step + 1 ..current_step {
753
+ let skipped_primary = step_proposer ( validators, header. parent_hash ( ) , step) ;
754
+ // Do not report this signer.
755
+ if skipped_primary != me {
756
+ // Stop reporting once validators start repeating.
757
+ if !reported. insert ( skipped_primary) { break ; }
758
+ self . validators . report_benign ( & skipped_primary, set_number, header. number ( ) ) ;
759
+ }
760
+ }
761
+ }
762
+ }
700
763
}
701
764
702
765
fn unix_now ( ) -> Duration {
@@ -876,32 +939,15 @@ impl Engine<EthereumMachine> for AuthorityRound {
876
939
return Seal :: None ;
877
940
}
878
941
879
- // fetch correct validator set for current epoch, taking into account
880
- // finality of previous transitions.
881
- let active_set;
882
-
883
- let validators = if self . immediate_transitions {
884
- & * self . validators
885
- } else {
886
- let mut epoch_manager = self . epoch_manager . lock ( ) ;
887
- let client = match self . client . read ( ) . as_ref ( ) . and_then ( |weak| weak. upgrade ( ) ) {
888
- Some ( client) => client,
889
- None => {
890
- warn ! ( target: "engine" , "Unable to generate seal: missing client ref." ) ;
891
- return Seal :: None ;
892
- }
893
- } ;
894
-
895
- if !epoch_manager. zoom_to ( & * client, & self . machine , & * self . validators , header) {
896
- debug ! ( target: "engine" , "Unable to zoom to epoch." ) ;
942
+ let ( validators, set_number) = match self . epoch_set ( header) {
943
+ Err ( err) => {
944
+ warn ! ( target: "engine" , "Unable to generate seal: {}" , err) ;
897
945
return Seal :: None ;
898
- }
899
-
900
- active_set = epoch_manager. validators ( ) . clone ( ) ;
901
- & active_set as & _
946
+ } ,
947
+ Ok ( ok) => ok,
902
948
} ;
903
949
904
- if is_step_proposer ( validators, header. parent_hash ( ) , step, header. author ( ) ) {
950
+ if is_step_proposer ( & * validators, header. parent_hash ( ) , step, header. author ( ) ) {
905
951
// this is guarded against by `can_propose` unless the block was signed
906
952
// on the same step (implies same key) and on a different node.
907
953
if parent_step == step. into ( ) {
@@ -932,9 +978,15 @@ impl Engine<EthereumMachine> for AuthorityRound {
932
978
933
979
// only issue the seal if we were the first to reach the compare_and_swap.
934
980
if self . step . can_propose . compare_and_swap ( true , false , AtomicOrdering :: SeqCst ) {
935
-
981
+ // we can drop all accumulated empty step messages that are
982
+ // older than the parent step since we're including them in
983
+ // the seal
936
984
self . clear_empty_steps ( parent_step) ;
937
985
986
+ // report any skipped primaries between the parent block and
987
+ // the block we're sealing
988
+ self . report_skipped ( header, step, u64:: from ( parent_step) as usize , & * validators, set_number) ;
989
+
938
990
let mut fields = vec ! [
939
991
encode( & step) . into_vec( ) ,
940
992
encode( & ( & H520 :: from( signature) as & [ u8 ] ) ) . into_vec( ) ,
@@ -1057,13 +1109,21 @@ impl Engine<EthereumMachine> for AuthorityRound {
1057
1109
) ) ) ;
1058
1110
}
1059
1111
1060
- // TODO [ToDr] Should this go from epoch manager?
1061
- // If yes then probably benign reporting needs to be moved further in the verification.
1062
- let set_number = header. number ( ) ;
1063
-
1064
1112
match verify_timestamp ( & self . step . inner , header_step ( header, self . empty_steps_transition ) ?) {
1065
1113
Err ( BlockError :: InvalidSeal ) => {
1066
- self . validators . report_benign ( header. author ( ) , set_number, header. number ( ) ) ;
1114
+ // This check runs in Phase 1 where there is no guarantee that the parent block is
1115
+ // already imported, therefore the call to `epoch_set` may fail. In that case we
1116
+ // won't report the misbehavior but this is not a concern because:
1117
+ // - Only authorities can report and it's expected that they'll be up-to-date and
1118
+ // importing, therefore the parent header will most likely be available
1119
+ // - Even if you are an authority that is syncing the chain, the contract will most
1120
+ // likely ignore old reports
1121
+ // - This specific check is only relevant if you're importing (since it checks
1122
+ // against wall clock)
1123
+ if let Ok ( ( _, set_number) ) = self . epoch_set ( header) {
1124
+ self . validators . report_benign ( header. author ( ) , set_number, header. number ( ) ) ;
1125
+ }
1126
+
1067
1127
Err ( BlockError :: InvalidSeal . into ( ) )
1068
1128
}
1069
1129
Err ( e) => Err ( e. into ( ) ) ,
@@ -1075,8 +1135,8 @@ impl Engine<EthereumMachine> for AuthorityRound {
1075
1135
fn verify_block_family ( & self , header : & Header , parent : & Header ) -> Result < ( ) , Error > {
1076
1136
let step = header_step ( header, self . empty_steps_transition ) ?;
1077
1137
let parent_step = header_step ( parent, self . empty_steps_transition ) ?;
1078
- // TODO [ToDr] Should this go from epoch manager?
1079
- let set_number = header . number ( ) ;
1138
+
1139
+ let ( validators , set_number) = self . epoch_set ( header ) ? ;
1080
1140
1081
1141
// Ensure header is from the step after parent.
1082
1142
if step == parent_step
@@ -1103,7 +1163,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
1103
1163
format ! ( "empty step proof for invalid parent hash: {:?}" , empty_step. parent_hash) ) ) ?;
1104
1164
}
1105
1165
1106
- if !empty_step. verify ( & * self . validators ) . unwrap_or ( false ) {
1166
+ if !empty_step. verify ( & * validators) . unwrap_or ( false ) {
1107
1167
Err ( EngineError :: InsufficientProof (
1108
1168
format ! ( "invalid empty step proof: {:?}" , empty_step) ) ) ?;
1109
1169
}
@@ -1117,59 +1177,29 @@ impl Engine<EthereumMachine> for AuthorityRound {
1117
1177
}
1118
1178
1119
1179
} else {
1120
- // Report skipped primaries.
1121
- if let ( true , Some ( me) ) = ( step > parent_step + 1 , self . signer . read ( ) . address ( ) ) {
1122
- debug ! ( target: "engine" , "Author {} built block with step gap. current step: {}, parent step: {}" ,
1123
- header. author( ) , step, parent_step) ;
1124
- let mut reported = HashSet :: new ( ) ;
1125
- for s in parent_step + 1 ..step {
1126
- let skipped_primary = step_proposer ( & * self . validators , & parent. hash ( ) , s) ;
1127
- // Do not report this signer.
1128
- if skipped_primary != me {
1129
- self . validators . report_benign ( & skipped_primary, set_number, header. number ( ) ) ;
1130
- // Stop reporting once validators start repeating.
1131
- if !reported. insert ( skipped_primary) { break ; }
1132
- }
1133
- }
1134
- }
1180
+ self . report_skipped ( header, step, parent_step, & * validators, set_number) ;
1135
1181
}
1136
1182
1137
1183
Ok ( ( ) )
1138
1184
}
1139
1185
1140
1186
// Check the validators.
1141
1187
fn verify_block_external ( & self , header : & Header ) -> Result < ( ) , Error > {
1142
- // fetch correct validator set for current epoch, taking into account
1143
- // finality of previous transitions.
1144
- let active_set;
1145
- let validators = if self . immediate_transitions {
1146
- & * self . validators
1147
- } else {
1148
- // get correct validator set for epoch.
1149
- let client = match self . client . read ( ) . as_ref ( ) . and_then ( |weak| weak. upgrade ( ) ) {
1150
- Some ( client) => client,
1151
- None => {
1152
- debug ! ( target: "engine" , "Unable to verify sig: missing client ref." ) ;
1153
- return Err ( EngineError :: RequiresClient . into ( ) )
1154
- }
1155
- } ;
1156
-
1157
- let mut epoch_manager = self . epoch_manager . lock ( ) ;
1158
- if !epoch_manager. zoom_to ( & * client, & self . machine , & * self . validators , header) {
1159
- debug ! ( target: "engine" , "Unable to zoom to epoch." ) ;
1160
- return Err ( EngineError :: RequiresClient . into ( ) )
1161
- }
1162
-
1163
- active_set = epoch_manager. validators ( ) . clone ( ) ;
1164
- & active_set as & _
1165
- } ;
1188
+ let ( validators, set_number) = self . epoch_set ( header) ?;
1166
1189
1167
1190
// verify signature against fixed list, but reports should go to the
1168
1191
// contract itself.
1169
- let res = verify_external ( header, validators, self . empty_steps_transition ) ;
1170
- if res. is_ok ( ) {
1171
- let header_step = header_step ( header, self . empty_steps_transition ) ?;
1172
- self . clear_empty_steps ( header_step. into ( ) ) ;
1192
+ let res = verify_external ( header, & * validators, self . empty_steps_transition ) ;
1193
+ match res {
1194
+ Err ( Error ( ErrorKind :: Engine ( EngineError :: NotProposer ( _) ) , _) ) => {
1195
+ self . validators . report_benign ( header. author ( ) , set_number, header. number ( ) ) ;
1196
+ } ,
1197
+ Ok ( _) => {
1198
+ // we can drop all accumulated empty step messages that are older than this header's step
1199
+ let header_step = header_step ( header, self . empty_steps_transition ) ?;
1200
+ self . clear_empty_steps ( header_step. into ( ) ) ;
1201
+ } ,
1202
+ _ => { } ,
1173
1203
}
1174
1204
res
1175
1205
}
@@ -1578,7 +1608,6 @@ mod tests {
1578
1608
parent_header. set_seal ( vec ! [ encode( & 1usize ) . into_vec( ) ] ) ;
1579
1609
parent_header. set_gas_limit ( "222222" . parse :: < U256 > ( ) . unwrap ( ) ) ;
1580
1610
let mut header: Header = Header :: default ( ) ;
1581
- header. set_number ( 1 ) ;
1582
1611
header. set_gas_limit ( "222222" . parse :: < U256 > ( ) . unwrap ( ) ) ;
1583
1612
header. set_seal ( vec ! [ encode( & 3usize ) . into_vec( ) ] ) ;
1584
1613
@@ -1588,8 +1617,15 @@ mod tests {
1588
1617
1589
1618
aura. set_signer ( Arc :: new ( AccountProvider :: transient_provider ( ) ) , Default :: default ( ) , "" . into ( ) ) ;
1590
1619
1620
+ // Do not report on steps skipped between genesis and first block.
1621
+ header. set_number ( 1 ) ;
1622
+ assert ! ( aura. verify_block_family( & header, & parent_header) . is_ok( ) ) ;
1623
+ assert_eq ! ( last_benign. load( AtomicOrdering :: SeqCst ) , 0 ) ;
1624
+
1625
+ // Report on skipped steps otherwise.
1626
+ header. set_number ( 2 ) ;
1591
1627
assert ! ( aura. verify_block_family( & header, & parent_header) . is_ok( ) ) ;
1592
- assert_eq ! ( last_benign. load( AtomicOrdering :: SeqCst ) , 1 ) ;
1628
+ assert_eq ! ( last_benign. load( AtomicOrdering :: SeqCst ) , 2 ) ;
1593
1629
}
1594
1630
1595
1631
#[ test]
0 commit comments