@@ -49,7 +49,8 @@ use crate::{
49
49
notification, round, Client ,
50
50
} ;
51
51
use beefy_primitives:: {
52
- BeefyApi , Commitment , ConsensusLog , MmrRootHash , SignedCommitment , ValidatorSet , BEEFY_ENGINE_ID , KEY_TYPE ,
52
+ BeefyApi , Commitment , ConsensusLog , MmrRootHash , SignedCommitment , ValidatorSet , BEEFY_ENGINE_ID ,
53
+ GENESIS_AUTHORITY_SET_ID , KEY_TYPE ,
53
54
} ;
54
55
55
56
/// The maximum number of live gossip rounds allowed, i.e. we will expire messages older than this.
@@ -163,17 +164,6 @@ struct VoteMessage<Hash, Number, Id, Signature> {
163
164
signature : Signature ,
164
165
}
165
166
166
- #[ derive( PartialEq ) ]
167
- /// Worker lifecycle state
168
- enum State {
169
- /// A new worker that still needs to be initialized.
170
- New ,
171
- /// A worker that validates and votes for commitments
172
- Validate ,
173
- /// A worker that acts as a goosip relay only
174
- Gossip ,
175
- }
176
-
177
167
pub ( crate ) struct BeefyWorker < B , C , BE , P >
178
168
where
179
169
B : Block ,
@@ -183,19 +173,21 @@ where
183
173
P :: Signature : Clone + Codec + Debug + PartialEq + TryFrom < Vec < u8 > > ,
184
174
C : Client < B , BE , P > ,
185
175
{
186
- state : State ,
187
- local_id : Option < P :: Public > ,
176
+ client : Arc < C > ,
188
177
key_store : SyncCryptoStorePtr ,
189
- min_interval : u32 ,
178
+ signed_commitment_sender : notification:: BeefySignedCommitmentSender < B , P :: Signature > ,
179
+ gossip_engine : Arc < Mutex < GossipEngine < B > > > ,
180
+ gossip_validator : Arc < BeefyGossipValidator < B , P > > ,
181
+ metrics : Option < Metrics > ,
190
182
rounds : round:: Rounds < MmrRootHash , NumberFor < B > , P :: Public , P :: Signature > ,
191
183
finality_notifications : FinalityNotifications < B > ,
192
- gossip_engine : Arc < Mutex < GossipEngine < B > > > ,
193
- signed_commitment_sender : notification:: BeefySignedCommitmentSender < B , P :: Signature > ,
194
- best_finalized_block : NumberFor < B > ,
184
+ min_interval : u32 ,
185
+ /// Best block we received a GRANDPA notification for
186
+ best_grandpa_block : NumberFor < B > ,
187
+ /// Best block a BEEFY voting round has been concluded for
188
+ best_beefy_block : Option < NumberFor < B > > ,
189
+ /// Best block this node has voted for
195
190
best_block_voted_on : NumberFor < B > ,
196
- client : Arc < C > ,
197
- metrics : Option < Metrics > ,
198
- gossip_validator : Arc < BeefyGossipValidator < B , P > > ,
199
191
_backend : PhantomData < BE > ,
200
192
_pair : PhantomData < P > ,
201
193
}
@@ -228,62 +220,22 @@ where
228
220
metrics : Option < Metrics > ,
229
221
) -> Self {
230
222
BeefyWorker {
231
- state : State :: New ,
232
- local_id : None ,
223
+ client : client. clone ( ) ,
233
224
key_store,
234
- min_interval : 2 ,
225
+ signed_commitment_sender,
226
+ gossip_engine : Arc :: new ( Mutex :: new ( gossip_engine) ) ,
227
+ gossip_validator,
228
+ metrics,
235
229
rounds : round:: Rounds :: new ( ValidatorSet :: empty ( ) ) ,
236
230
finality_notifications : client. finality_notification_stream ( ) ,
237
- gossip_engine : Arc :: new ( Mutex :: new ( gossip_engine ) ) ,
238
- signed_commitment_sender ,
239
- best_finalized_block : Zero :: zero ( ) ,
231
+ min_interval : 2 ,
232
+ best_grandpa_block : client . info ( ) . finalized_number ,
233
+ best_beefy_block : None ,
240
234
best_block_voted_on : Zero :: zero ( ) ,
241
- client,
242
- metrics,
243
- gossip_validator,
244
235
_backend : PhantomData ,
245
236
_pair : PhantomData ,
246
237
}
247
238
}
248
-
249
- fn init_validator_set ( & mut self ) -> Result < ( ) , error:: Lifecycle > {
250
- let at = BlockId :: hash ( self . client . info ( ) . best_hash ) ;
251
-
252
- let validator_set = self
253
- . client
254
- . runtime_api ( )
255
- . validator_set ( & at)
256
- . map_err ( |err| error:: Lifecycle :: MissingValidatorSet ( err. to_string ( ) ) ) ?;
257
-
258
- let local_id = match validator_set
259
- . validators
260
- . iter ( )
261
- . find ( |id| SyncCryptoStore :: has_keys ( & * self . key_store , & [ ( id. to_raw_vec ( ) , KEY_TYPE ) ] ) )
262
- {
263
- Some ( id) => {
264
- info ! ( target: "beefy" , "🥩 Starting BEEFY worker with local id: {:?}" , id) ;
265
- self . state = State :: Validate ;
266
- Some ( id. clone ( ) )
267
- }
268
- None => {
269
- info ! ( target: "beefy" , "🥩 No local id found, BEEFY worker will be gossip only." ) ;
270
- self . state = State :: Gossip ;
271
- None
272
- }
273
- } ;
274
-
275
- self . local_id = local_id;
276
- self . rounds = round:: Rounds :: new ( validator_set. clone ( ) ) ;
277
-
278
- // we are actually interested in the best finalized block with the BEEFY pallet
279
- // being available on-chain. That is why we set `best_finalized_block` here and
280
- // not as part of `new()` already.
281
- self . best_finalized_block = self . client . info ( ) . finalized_number ;
282
-
283
- debug ! ( target: "beefy" , "🥩 Validator set with id {} initialized" , validator_set. id) ;
284
-
285
- Ok ( ( ) )
286
- }
287
239
}
288
240
289
241
impl < B , C , BE , P > BeefyWorker < B , C , BE , P >
@@ -296,15 +248,18 @@ where
296
248
C : Client < B , BE , P > ,
297
249
C :: Api : BeefyApi < B , P :: Public > ,
298
250
{
251
+ /// Return `true`, if the should vote on block `number`
299
252
fn should_vote_on ( & self , number : NumberFor < B > ) -> bool {
300
253
use sp_runtime:: { traits:: Saturating , SaturatedConversion } ;
301
254
302
- // we only vote as a validator
303
- if self . state != State :: Validate {
255
+ let best_beefy_block = if let Some ( block) = self . best_beefy_block {
256
+ block
257
+ } else {
258
+ debug ! ( target: "beefy" , "🥩 Missing best BEEFY block - won't vote for: {:?}" , number) ;
304
259
return false ;
305
- }
260
+ } ;
306
261
307
- let diff = self . best_finalized_block . saturating_sub ( self . best_block_voted_on ) ;
262
+ let diff = self . best_grandpa_block . saturating_sub ( best_beefy_block ) ;
308
263
let diff = diff. saturated_into :: < u32 > ( ) ;
309
264
let next_power_of_two = ( diff / 2 ) . next_power_of_two ( ) ;
310
265
let next_block_to_vote_on = self . best_block_voted_on + self . min_interval . max ( next_power_of_two) . into ( ) ;
@@ -334,26 +289,61 @@ where
334
289
Ok ( sig)
335
290
}
336
291
292
+ /// Return the current active validator set at header `header`.
293
+ ///
294
+ /// Note that the validator set could be `None`. This is the case if we don't find
295
+ /// a BEEFY authority set change and we can't fetch the validator set from the
296
+ /// BEEFY on-chain state. Such a failure is usually an indication that the BEEFT
297
+ /// pallet has not been deployed (yet).
298
+ fn validator_set ( & self , header : & B :: Header ) -> Option < ValidatorSet < P :: Public > > {
299
+ if let Some ( new) = find_authorities_change :: < B , P :: Public > ( header) {
300
+ Some ( new)
301
+ } else {
302
+ let at = BlockId :: hash ( header. hash ( ) ) ;
303
+ self . client . runtime_api ( ) . validator_set ( & at) . ok ( )
304
+ }
305
+ }
306
+
307
+ /// Return the local authority id.
308
+ ///
309
+ /// `None` is returned, if we are not permitted to vote
310
+ fn local_id ( & self ) -> Option < P :: Public > {
311
+ self . rounds
312
+ . validators ( )
313
+ . iter ( )
314
+ . find ( |id| SyncCryptoStore :: has_keys ( & * self . key_store , & [ ( id. to_raw_vec ( ) , KEY_TYPE ) ] ) )
315
+ . cloned ( )
316
+ }
317
+
337
318
fn handle_finality_notification ( & mut self , notification : FinalityNotification < B > ) {
338
319
debug ! ( target: "beefy" , "🥩 Finality notification: {:?}" , notification) ;
339
320
340
- if let Some ( new) = find_authorities_change :: < B , P :: Public > ( & notification. header ) {
341
- debug ! ( target: "beefy" , "🥩 New validator set: {:?}" , new) ;
321
+ // update best GRANDPA finalized block we have seen
322
+ self . best_grandpa_block = * notification. header . number ( ) ;
323
+
324
+ if let Some ( active) = self . validator_set ( & notification. header ) {
325
+ debug ! ( target: "beefy" , "🥩 Active validator set id: {:?}" , active) ;
342
326
343
327
if let Some ( metrics) = self . metrics . as_ref ( ) {
344
- metrics. beefy_validator_set_id . set ( new . id ) ;
328
+ metrics. beefy_validator_set_id . set ( active . id ) ;
345
329
}
346
330
347
- self . rounds = round:: Rounds :: new ( new) ;
331
+ // Authority set change or genesis set id triggers new voting rounds
332
+ //
333
+ // TODO: (adoerr) Enacting a new authority set will also implicitly 'conclude'
334
+ // the currently active BEEFY voting round by starting a new one. This is
335
+ // temporary and needs to be repalced by proper round life cycle handling.
336
+ if ( active. id != self . rounds . validator_set_id ( ) ) || ( active. id == GENESIS_AUTHORITY_SET_ID ) {
337
+ self . rounds = round:: Rounds :: new ( active. clone ( ) ) ;
338
+
339
+ debug ! ( target: "beefy" , "🥩 New Rounds for id: {:?}" , active. id) ;
348
340
349
- // NOTE: currently we act as if this block has been finalized by BEEFY as we perform
350
- // the validator set changes instantly (insecure). Once proper validator set changes
351
- // are implemented this should be removed
352
- self . best_finalized_block = * notification. header . number ( ) ;
341
+ self . best_beefy_block = Some ( * notification. header . number ( ) ) ;
342
+ }
353
343
} ;
354
344
355
345
if self . should_vote_on ( * notification. header . number ( ) ) {
356
- let local_id = if let Some ( ref id) = self . local_id {
346
+ let local_id = if let Some ( id) = self . local_id ( ) {
357
347
id
358
348
} else {
359
349
error ! ( target: "beefy" , "🥩 Missing validator id - can't vote for: {:?}" , notification. header. hash( ) ) ;
@@ -385,7 +375,7 @@ where
385
375
386
376
let message = VoteMessage {
387
377
commitment,
388
- id : local_id. clone ( ) ,
378
+ id : local_id,
389
379
signature,
390
380
} ;
391
381
@@ -426,7 +416,7 @@ where
426
416
info ! ( target: "beefy" , "🥩 Round #{} concluded, committed: {:?}." , round. 1 , signed_commitment) ;
427
417
428
418
self . signed_commitment_sender . notify ( signed_commitment) ;
429
- self . best_finalized_block = round. 1 ;
419
+ self . best_beefy_block = Some ( round. 1 ) ;
430
420
}
431
421
}
432
422
}
@@ -450,16 +440,6 @@ where
450
440
futures:: select! {
451
441
notification = self . finality_notifications. next( ) . fuse( ) => {
452
442
if let Some ( notification) = notification {
453
- if self . state == State :: New {
454
- match self . init_validator_set( ) {
455
- Ok ( ( ) ) => ( ) ,
456
- Err ( err) => {
457
- // this is not treated as an error here because there really is
458
- // nothing a node operator could do in order to remedy the root cause.
459
- debug!( target: "beefy" , "🥩 Init validator set failed: {:?}" , err) ;
460
- }
461
- }
462
- }
463
443
self . handle_finality_notification( notification) ;
464
444
} else {
465
445
return ;
0 commit comments