1
+ use futures:: future:: pending;
1
2
use sc_client_api:: AuxStore ;
2
3
use sc_client_api:: BlockOf ;
3
4
use sc_client_api:: UsageProvider ;
@@ -8,17 +9,23 @@ use sc_consensus::{BasicQueue, DefaultImportQueue};
8
9
use sc_consensus_aura:: AuraVerifier ;
9
10
use sc_consensus_aura:: CheckForEquivocation ;
10
11
use sc_consensus_aura:: ImportQueueParams ;
12
+ use sc_consensus_babe:: CompatibleDigestItem as _;
11
13
use sc_consensus_slots:: InherentDataProviderExt ;
12
14
use sc_telemetry:: TelemetryHandle ;
13
15
use sp_api:: ApiExt ;
14
16
use sp_api:: ProvideRuntimeApi ;
15
17
use sp_block_builder:: BlockBuilder as BlockBuilderApi ;
16
18
use sp_blockchain:: HeaderBackend ;
19
+ use sp_blockchain:: HeaderMetadata ;
17
20
use sp_consensus:: error:: Error as ConsensusError ;
18
21
use sp_consensus_aura:: AuraApi ;
19
- use sp_consensus_aura:: sr25519:: AuthorityId ;
20
- use sp_consensus_aura:: sr25519:: AuthorityPair ;
22
+ use sp_consensus_aura:: sr25519:: AuthorityId as AuraAuthorityId ;
23
+ use sp_consensus_aura:: sr25519:: AuthorityPair as AuraAuthorityPair ;
24
+ use sp_consensus_babe:: AuthorityId as BabeAuthorityId ;
25
+ use sp_consensus_babe:: AuthorityPair as BabeAuthorityPair ;
21
26
use sp_consensus_babe:: BABE_ENGINE_ID ;
27
+ use sp_core:: Pair ;
28
+ use sp_core:: crypto:: Ss58Codec ;
22
29
use sp_inherents:: CreateInherentDataProviders ;
23
30
use sp_runtime:: Digest ;
24
31
use sp_runtime:: DigestItem ;
@@ -34,11 +41,19 @@ use std::sync::Arc;
34
41
/// blacklist the offending node and refuse to connect with them until they
35
42
/// are restarted
36
43
struct AuraWrappedVerifier < B , C , CIDP , N > {
37
- inner : AuraVerifier < C , AuthorityPair , CIDP , N > ,
44
+ inner : AuraVerifier < C , AuraAuthorityPair , CIDP , N > ,
45
+ client : Arc < C > ,
38
46
_phantom : std:: marker:: PhantomData < B > ,
39
47
}
40
48
41
- impl < B : BlockT , C , CIDP , N > AuraWrappedVerifier < B , C , CIDP , N > {
49
+ impl < B : BlockT , C , CIDP , N > AuraWrappedVerifier < B , C , CIDP , N >
50
+ where
51
+ CIDP : CreateInherentDataProviders < B , ( ) > + Send + Sync ,
52
+ CIDP :: InherentDataProviders : InherentDataProviderExt + Send + Sync ,
53
+ C : ProvideRuntimeApi < B > + Send + Sync + sc_client_api:: backend:: AuxStore ,
54
+ C :: Api : BlockBuilderApi < B > + AuraApi < B , AuraAuthorityId > + ApiExt < B > ,
55
+ C : HeaderBackend < B > + HeaderMetadata < B > ,
56
+ {
42
57
pub fn new (
43
58
client : Arc < C > ,
44
59
create_inherent_data_providers : CIDP ,
@@ -47,44 +62,102 @@ impl<B: BlockT, C, CIDP, N> AuraWrappedVerifier<B, C, CIDP, N> {
47
62
compatibility_mode : sc_consensus_aura:: CompatibilityMode < N > ,
48
63
) -> Self {
49
64
let verifier_params = sc_consensus_aura:: BuildVerifierParams :: < C , CIDP , _ > {
50
- client,
65
+ client : client . clone ( ) ,
51
66
create_inherent_data_providers,
52
67
telemetry,
53
68
check_for_equivocation,
54
69
compatibility_mode,
55
70
} ;
56
71
let verifier =
57
- sc_consensus_aura:: build_verifier :: < AuthorityPair , C , CIDP , N > ( verifier_params) ;
72
+ sc_consensus_aura:: build_verifier :: < AuraAuthorityPair , C , CIDP , N > ( verifier_params) ;
58
73
59
74
AuraWrappedVerifier {
60
75
inner : verifier,
76
+ client,
61
77
_phantom : std:: marker:: PhantomData ,
62
78
}
63
79
}
80
+
81
+ /// When a Babe block is encountered in Aura mode, we need to check it is legitimate
82
+ /// before switching to the Babe service.
83
+ ///
84
+ /// We can't use a full [`BabeVerifier`] because we don't have a Babe link running, however we
85
+ /// can check that the block author is one of the authorities from the last verified Aura block.
86
+ ///
87
+ /// The Babe block will be verified in full after the node spins back up as a Babe service.
88
+ async fn check_babe_block ( & self , block : BlockImportParams < B > ) -> Result < ( ) , String > {
89
+ log:: info!(
90
+ "Checking Babe block {:?} is legitimate" ,
91
+ block. post_header( ) . hash( )
92
+ ) ;
93
+ let mut header = block. header . clone ( ) ;
94
+ let seal = header
95
+ . digest_mut ( )
96
+ . pop ( )
97
+ . ok_or_else ( || "Header Unsealed" . to_string ( ) ) ?;
98
+ let sig = seal
99
+ . as_babe_seal ( )
100
+ . ok_or_else ( || "Header bad seal" . to_string ( ) ) ?;
101
+
102
+ let authorities = self . get_last_aura_authorities ( block. header ) ?;
103
+ if let Some ( a) = authorities. into_iter ( ) . find ( |a| {
104
+ let babe_key = BabeAuthorityId :: from ( a. clone ( ) . into_inner ( ) ) ;
105
+ BabeAuthorityPair :: verify ( & sig, header. hash ( ) , & babe_key)
106
+ } ) {
107
+ log:: info!(
108
+ "Babe block has a valid signature by author: {}" ,
109
+ a. to_ss58check( )
110
+ ) ;
111
+ Ok ( ( ) )
112
+ } else {
113
+ Err ( "Babe block has a bad signature. Rejecting." . to_string ( ) )
114
+ }
115
+ }
116
+
117
+ /// Given the hash of the first Babe block mined, get the Aura authorities that existed prior to
118
+ /// the runtime upgrade.
119
+ ///
120
+ /// Note: We need get the Aura authorities from grandparent rather than the parent,
121
+ /// because the runtime upgrade clearing the Aura authorities occurs in the parent.
122
+ fn get_last_aura_authorities (
123
+ & self ,
124
+ first_babe_block_header : B :: Header ,
125
+ ) -> Result < Vec < AuraAuthorityId > , String > {
126
+ let parent_header = self
127
+ . client
128
+ . header ( * first_babe_block_header. parent_hash ( ) )
129
+ . map_err ( |e| format ! ( "Failed to get parent header: {}" , e) ) ?
130
+ . ok_or ( "Parent header not found" . to_string ( ) ) ?;
131
+ let grandparent_hash = parent_header. parent_hash ( ) ;
132
+
133
+ let runtime_api = self . client . runtime_api ( ) ;
134
+ let authorities = runtime_api
135
+ . authorities ( * grandparent_hash)
136
+ . map_err ( |e| format ! ( "Failed to get Aura authorities: {}" , e) ) ?;
137
+
138
+ Ok ( authorities)
139
+ }
64
140
}
65
141
66
142
#[ async_trait:: async_trait]
67
143
impl < B : BlockT , C , CIDP > Verifier < B > for AuraWrappedVerifier < B , C , CIDP , NumberFor < B > >
68
144
where
69
145
C : ProvideRuntimeApi < B > + Send + Sync + sc_client_api:: backend:: AuxStore ,
70
- C :: Api : BlockBuilderApi < B > + AuraApi < B , AuthorityId > + ApiExt < B > ,
146
+ C :: Api : BlockBuilderApi < B > + AuraApi < B , AuraAuthorityId > + ApiExt < B > ,
147
+ C : HeaderBackend < B > + HeaderMetadata < B > ,
71
148
CIDP : CreateInherentDataProviders < B , ( ) > + Send + Sync ,
72
149
CIDP :: InherentDataProviders : InherentDataProviderExt + Send + Sync ,
73
150
{
74
151
async fn verify ( & self , block : BlockImportParams < B > ) -> Result < BlockImportParams < B > , String > {
75
152
let number: NumberFor < B > = * block. post_header ( ) . number ( ) ;
76
153
log:: debug!( "Verifying block: {:?}" , number) ;
77
154
if is_babe_digest ( block. header . digest ( ) ) {
78
- // TODO: Use a BabeVerifier to verify Babe blocks. This will
79
- // prevent rapid validation failure and subsequent re-fetching
80
- // of the same block from peers, which triggers the peers to
81
- // blacklist the offending node and refuse to connect with them until they
82
- // are restarted.
83
- //
84
- // Unfortunately, BabeVerifier construction logic is NOT public outside of
85
- // its crate in vanilla Polkadot SDK, so we are unable to use it until we
86
- // migrate to our Polkadot SDK fork.
87
- self . inner . verify ( block) . await
155
+ self . check_babe_block ( block) . await ?;
156
+ log:: debug!(
157
+ "Detected Babe block! Verifier cannot continue, upgrade must be triggered elsewhere..."
158
+ ) ;
159
+ pending :: < ( ) > ( ) . await ;
160
+ unreachable ! ( "Should not reach here, pending forever." ) ;
88
161
} else {
89
162
self . inner . verify ( block) . await
90
163
}
@@ -97,15 +170,16 @@ pub fn import_queue<B, I, C, S, CIDP>(
97
170
) -> Result < DefaultImportQueue < B > , sp_consensus:: Error >
98
171
where
99
172
B : BlockT ,
100
- C :: Api : BlockBuilderApi < B > + AuraApi < B , AuthorityId > + ApiExt < B > ,
173
+ C :: Api : BlockBuilderApi < B > + AuraApi < B , AuraAuthorityId > + ApiExt < B > ,
101
174
C : ' static
102
175
+ ProvideRuntimeApi < B >
103
176
+ BlockOf
104
177
+ Send
105
178
+ Sync
106
179
+ AuxStore
107
180
+ UsageProvider < B >
108
- + HeaderBackend < B > ,
181
+ + HeaderBackend < B >
182
+ + HeaderMetadata < B > ,
109
183
I : BlockImport < B , Error = ConsensusError > + Send + Sync + ' static ,
110
184
S : sp_core:: traits:: SpawnEssentialNamed ,
111
185
CIDP : CreateInherentDataProviders < B , ( ) > + Sync + Send + ' static ,
0 commit comments