Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 75ea15d

Browse files
authored
Milestone 1 (#144)
* use best_finalized, prevent race * make best_finalized_block an Option, should_vote_on bails on None * Bump futures from 0.3.13 to 0.3.14 * Revert futures bump * Revert "Revert futures bump" This reverts commit a1b5e7e9bac526f2897ebfdfee7f02dd29a13ac5. * Revert "Bump futures from 0.3.13 to 0.3.14" This reverts commit a4e508b118ad2c4b52909d24143c284073961458. * debug msg if the bail voting * validator_set() * local_id() * get rid of worker state * Apply review suggestions * fix should_vote_on()
1 parent d3dd36e commit 75ea15d

File tree

4 files changed

+82
-103
lines changed

4 files changed

+82
-103
lines changed

client/beefy/src/error.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,3 @@ pub(crate) enum Crypto<Id: Public + Debug> {
3232
#[error("Failed to sign comitment using key: {0:?}. Reason: {1}")]
3333
CannotSign(Id, String),
3434
}
35-
36-
/// Lifecycle related errors
37-
#[derive(Debug, thiserror::Error)]
38-
pub(crate) enum Lifecycle {
39-
/// Can't fetch validator set from BEEFY pallet
40-
#[error("Failed to fetch validator set: {0}")]
41-
MissingValidatorSet(String),
42-
}

client/beefy/src/round.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,17 @@ impl<Hash, Number, Id, Signature> Rounds<Hash, Number, Id, Signature>
7575
where
7676
Hash: Ord,
7777
Number: Ord,
78-
Id: PartialEq,
78+
Id: PartialEq + Clone,
7979
Signature: Clone + PartialEq,
8080
{
8181
pub(crate) fn validator_set_id(&self) -> ValidatorSetId {
8282
self.validator_set.id
8383
}
8484

85+
pub(crate) fn validators(&self) -> Vec<Id> {
86+
self.validator_set.validators.clone()
87+
}
88+
8589
pub(crate) fn add_vote(&mut self, round: (Hash, Number), vote: (Id, Signature)) -> bool {
8690
self.rounds.entry(round).or_default().add_vote(vote)
8791
}

client/beefy/src/worker.rs

Lines changed: 74 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ use crate::{
4949
notification, round, Client,
5050
};
5151
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,
5354
};
5455

5556
/// 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> {
163164
signature: Signature,
164165
}
165166

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-
177167
pub(crate) struct BeefyWorker<B, C, BE, P>
178168
where
179169
B: Block,
@@ -183,19 +173,21 @@ where
183173
P::Signature: Clone + Codec + Debug + PartialEq + TryFrom<Vec<u8>>,
184174
C: Client<B, BE, P>,
185175
{
186-
state: State,
187-
local_id: Option<P::Public>,
176+
client: Arc<C>,
188177
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>,
190182
rounds: round::Rounds<MmrRootHash, NumberFor<B>, P::Public, P::Signature>,
191183
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
195190
best_block_voted_on: NumberFor<B>,
196-
client: Arc<C>,
197-
metrics: Option<Metrics>,
198-
gossip_validator: Arc<BeefyGossipValidator<B, P>>,
199191
_backend: PhantomData<BE>,
200192
_pair: PhantomData<P>,
201193
}
@@ -228,62 +220,22 @@ where
228220
metrics: Option<Metrics>,
229221
) -> Self {
230222
BeefyWorker {
231-
state: State::New,
232-
local_id: None,
223+
client: client.clone(),
233224
key_store,
234-
min_interval: 2,
225+
signed_commitment_sender,
226+
gossip_engine: Arc::new(Mutex::new(gossip_engine)),
227+
gossip_validator,
228+
metrics,
235229
rounds: round::Rounds::new(ValidatorSet::empty()),
236230
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,
240234
best_block_voted_on: Zero::zero(),
241-
client,
242-
metrics,
243-
gossip_validator,
244235
_backend: PhantomData,
245236
_pair: PhantomData,
246237
}
247238
}
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-
}
287239
}
288240

289241
impl<B, C, BE, P> BeefyWorker<B, C, BE, P>
@@ -296,15 +248,18 @@ where
296248
C: Client<B, BE, P>,
297249
C::Api: BeefyApi<B, P::Public>,
298250
{
251+
/// Return `true`, if the should vote on block `number`
299252
fn should_vote_on(&self, number: NumberFor<B>) -> bool {
300253
use sp_runtime::{traits::Saturating, SaturatedConversion};
301254

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);
304259
return false;
305-
}
260+
};
306261

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);
308263
let diff = diff.saturated_into::<u32>();
309264
let next_power_of_two = (diff / 2).next_power_of_two();
310265
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
334289
Ok(sig)
335290
}
336291

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+
337318
fn handle_finality_notification(&mut self, notification: FinalityNotification<B>) {
338319
debug!(target: "beefy", "🥩 Finality notification: {:?}", notification);
339320

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);
342326

343327
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);
345329
}
346330

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);
348340

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+
}
353343
};
354344

355345
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() {
357347
id
358348
} else {
359349
error!(target: "beefy", "🥩 Missing validator id - can't vote for: {:?}", notification.header.hash());
@@ -385,7 +375,7 @@ where
385375

386376
let message = VoteMessage {
387377
commitment,
388-
id: local_id.clone(),
378+
id: local_id,
389379
signature,
390380
};
391381

@@ -426,7 +416,7 @@ where
426416
info!(target: "beefy", "🥩 Round #{} concluded, committed: {:?}.", round.1, signed_commitment);
427417

428418
self.signed_commitment_sender.notify(signed_commitment);
429-
self.best_finalized_block = round.1;
419+
self.best_beefy_block = Some(round.1);
430420
}
431421
}
432422
}
@@ -450,16 +440,6 @@ where
450440
futures::select! {
451441
notification = self.finality_notifications.next().fuse() => {
452442
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-
}
463443
self.handle_finality_notification(notification);
464444
} else {
465445
return;

primitives/beefy/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ pub mod ecdsa {
6666
/// The `ConsensusEngineId` of BEEFY.
6767
pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
6868

69+
/// Authority set id starts with zero at genesis
70+
pub const GENESIS_AUTHORITY_SET_ID: u64 = 0;
71+
6972
/// A typedef for validator set id.
7073
pub type ValidatorSetId = u64;
7174

0 commit comments

Comments
 (0)