From 99df39194d5de45f46aa107fe5debb49ef2678db Mon Sep 17 00:00:00 2001
From: Serban Iorga <serban@parity.io>
Date: Fri, 15 Dec 2023 08:09:39 +0100
Subject: [PATCH] BEEFY: `expect_validator_set()` fix (#2716)

Fixes #https://github.com/paritytech/polkadot-sdk/issues/2699

Modifying `expect_validator_set()` in order to be able to walk back
until block 0. The chain state at block 0 is available even if we use
`--sync fast` or `--sync warp`. This way we can retrieve the initial
authority set even when BEEFY genesis is 1 and there is no authority
change entry in the headers log.

Credits to @acatangiu for the solution

---------

Co-authored-by: Adrian Catangiu <adrian@parity.io>
---
 substrate/client/consensus/beefy/src/lib.rs | 38 ++++++++++-----------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs
index b3ff11add27..c3da2b886f4 100644
--- a/substrate/client/consensus/beefy/src/lib.rs
+++ b/substrate/client/consensus/beefy/src/lib.rs
@@ -543,25 +543,23 @@ where
 	R: ProvideRuntimeApi<B>,
 	R::Api: BeefyApi<B, AuthorityId>,
 {
-	debug!(target: LOG_TARGET, "🥩 Try to find validator set active at header: {:?}", at_header);
-	runtime
-		.runtime_api()
-		.validator_set(at_header.hash())
-		.ok()
-		.flatten()
-		.or_else(|| {
-			// if state unavailable, fallback to walking up the chain looking for the header
-			// Digest emitted when validator set active 'at_header' was enacted.
-			let blockchain = backend.blockchain();
-			let mut header = at_header.clone();
-			loop {
-				debug!(target: LOG_TARGET, "🥩 look for auth set change digest in header number: {:?}", *header.number());
-				match worker::find_authorities_change::<B>(&header) {
-					Some(active) => return Some(active),
-					// Move up the chain.
-					None => header = blockchain.expect_header(*header.parent_hash()).ok()?,
-				}
+	let blockchain = backend.blockchain();
+
+	// Walk up the chain looking for the validator set active at 'at_header'. Process both state and
+	// header digests.
+	debug!(target: LOG_TARGET, "🥩 Trying to find validator set active at header: {:?}", at_header);
+	let mut header = at_header.clone();
+	loop {
+		if let Ok(Some(active)) = runtime.runtime_api().validator_set(at_header.hash()) {
+			return Ok(active)
+		} else {
+			debug!(target: LOG_TARGET, "🥩 Looking for auth set change at block number: {:?}", *header.number());
+			match worker::find_authorities_change::<B>(&header) {
+				Some(active) => return Ok(active),
+				// Move up the chain. Ultimately we'll get it from chain genesis state, or error out
+				// here.
+				None => header = blockchain.expect_header(*header.parent_hash())?,
 			}
-		})
-		.ok_or_else(|| ClientError::Backend("Could not find initial validator set".into()))
+		}
+	}
 }
-- 
GitLab