mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-23 22:31:06 +00:00
sc-consensus-beefy: graceful support for pallet-beefy reset (#14217)
BEEFY consensus can be restarted by resetting "genesisBlock" in pallet-beefy, but we don't want to also reset authority set IDs so that they are uniquely identified across the entire chain history regardless of how many times BEEFY consensus has been reset/restarted. This is why the client now also accepts initial authority_set_id != 0. BEEFY client now detects pallet-beefy reset/reinit and errors-out and asks for a restart. BEEFY client persisted state should be discarded on client restarts following pallet-beefy reset/reinit. End result is BEEFY client/voter can now completely reinitialize using "new" on-chain info following pallet-beefy reset/reinit, discarding old state. Fixes #14203 Fixes #14204 Signed-off-by: acatangiu <adrian@parity.io>
This commit is contained in:
@@ -47,7 +47,6 @@ use sp_blockchain::{
|
||||
use sp_consensus::{Error as ConsensusError, SyncOracle};
|
||||
use sp_consensus_beefy::{
|
||||
crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID,
|
||||
GENESIS_AUTHORITY_SET_ID,
|
||||
};
|
||||
use sp_keystore::KeystorePtr;
|
||||
use sp_mmr_primitives::MmrApi;
|
||||
@@ -282,8 +281,14 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
|
||||
let persisted_state =
|
||||
match wait_for_runtime_pallet(&*runtime, &mut gossip_engine, &mut finality_notifications)
|
||||
.await
|
||||
.and_then(|best_grandpa| {
|
||||
load_or_init_voter_state(&*backend, &*runtime, best_grandpa, min_block_delta)
|
||||
.and_then(|(beefy_genesis, best_grandpa)| {
|
||||
load_or_init_voter_state(
|
||||
&*backend,
|
||||
&*runtime,
|
||||
beefy_genesis,
|
||||
best_grandpa,
|
||||
min_block_delta,
|
||||
)
|
||||
}) {
|
||||
Ok(state) => state,
|
||||
Err(e) => {
|
||||
@@ -316,9 +321,9 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
|
||||
persisted_state,
|
||||
};
|
||||
|
||||
futures::future::join(
|
||||
worker.run(block_import_justif, finality_notifications),
|
||||
on_demand_justifications_handler.run(),
|
||||
futures::future::select(
|
||||
Box::pin(worker.run(block_import_justif, finality_notifications)),
|
||||
Box::pin(on_demand_justifications_handler.run()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -326,6 +331,7 @@ pub async fn start_beefy_gadget<B, BE, C, N, P, R, S>(
|
||||
fn load_or_init_voter_state<B, BE, R>(
|
||||
backend: &BE,
|
||||
runtime: &R,
|
||||
beefy_genesis: NumberFor<B>,
|
||||
best_grandpa: <B as Block>::Header,
|
||||
min_block_delta: u32,
|
||||
) -> ClientResult<PersistedState<B>>
|
||||
@@ -335,17 +341,22 @@ where
|
||||
R: ProvideRuntimeApi<B>,
|
||||
R::Api: BeefyApi<B>,
|
||||
{
|
||||
// Initialize voter state from AUX DB or from pallet genesis.
|
||||
if let Some(mut state) = crate::aux_schema::load_persistent(backend)? {
|
||||
// Overwrite persisted state with current best GRANDPA block.
|
||||
state.set_best_grandpa(best_grandpa);
|
||||
// Overwrite persisted data with newly provided `min_block_delta`.
|
||||
state.set_min_block_delta(min_block_delta);
|
||||
info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state);
|
||||
Ok(state)
|
||||
} else {
|
||||
initialize_voter_state(backend, runtime, best_grandpa, min_block_delta)
|
||||
}
|
||||
// Initialize voter state from AUX DB if compatible.
|
||||
crate::aux_schema::load_persistent(backend)?
|
||||
// Verify state pallet genesis matches runtime.
|
||||
.filter(|state| state.pallet_genesis() == beefy_genesis)
|
||||
.and_then(|mut state| {
|
||||
// Overwrite persisted state with current best GRANDPA block.
|
||||
state.set_best_grandpa(best_grandpa.clone());
|
||||
// Overwrite persisted data with newly provided `min_block_delta`.
|
||||
state.set_min_block_delta(min_block_delta);
|
||||
info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state);
|
||||
Some(Ok(state))
|
||||
})
|
||||
// No valid voter-state persisted, re-initialize from pallet genesis.
|
||||
.unwrap_or_else(|| {
|
||||
initialize_voter_state(backend, runtime, beefy_genesis, best_grandpa, min_block_delta)
|
||||
})
|
||||
}
|
||||
|
||||
// If no persisted state present, walk back the chain from first GRANDPA notification to either:
|
||||
@@ -355,6 +366,7 @@ where
|
||||
fn initialize_voter_state<B, BE, R>(
|
||||
backend: &BE,
|
||||
runtime: &R,
|
||||
beefy_genesis: NumberFor<B>,
|
||||
best_grandpa: <B as Block>::Header,
|
||||
min_block_delta: u32,
|
||||
) -> ClientResult<PersistedState<B>>
|
||||
@@ -369,6 +381,7 @@ where
|
||||
.beefy_genesis(best_grandpa.hash())
|
||||
.ok()
|
||||
.flatten()
|
||||
.filter(|genesis| *genesis == beefy_genesis)
|
||||
.ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into()))?;
|
||||
// Walk back the imported blocks and initialize voter either, at the last block with
|
||||
// a BEEFY justification, or at pallet genesis block; voter will resume from there.
|
||||
@@ -396,16 +409,20 @@ where
|
||||
rounds.conclude(best_beefy);
|
||||
sessions.push_front(rounds);
|
||||
}
|
||||
let state =
|
||||
PersistedState::checked_new(best_grandpa, best_beefy, sessions, min_block_delta)
|
||||
.ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?;
|
||||
let state = PersistedState::checked_new(
|
||||
best_grandpa,
|
||||
best_beefy,
|
||||
sessions,
|
||||
min_block_delta,
|
||||
beefy_genesis,
|
||||
)
|
||||
.ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?;
|
||||
break state
|
||||
}
|
||||
|
||||
if *header.number() == beefy_genesis {
|
||||
// We've reached BEEFY genesis, initialize voter here.
|
||||
let genesis_set =
|
||||
expect_validator_set(runtime, header.hash()).and_then(genesis_set_sanity_check)?;
|
||||
let genesis_set = expect_validator_set(runtime, header.hash())?;
|
||||
info!(
|
||||
target: LOG_TARGET,
|
||||
"🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \
|
||||
@@ -415,8 +432,14 @@ where
|
||||
);
|
||||
|
||||
sessions.push_front(Rounds::new(beefy_genesis, genesis_set));
|
||||
break PersistedState::checked_new(best_grandpa, Zero::zero(), sessions, min_block_delta)
|
||||
.ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?
|
||||
break PersistedState::checked_new(
|
||||
best_grandpa,
|
||||
Zero::zero(),
|
||||
sessions,
|
||||
min_block_delta,
|
||||
beefy_genesis,
|
||||
)
|
||||
.ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?
|
||||
}
|
||||
|
||||
if let Some(active) = worker::find_authorities_change::<B>(&header) {
|
||||
@@ -451,7 +474,7 @@ async fn wait_for_runtime_pallet<B, R>(
|
||||
runtime: &R,
|
||||
mut gossip_engine: &mut GossipEngine<B>,
|
||||
finality: &mut Fuse<FinalityNotifications<B>>,
|
||||
) -> ClientResult<<B as Block>::Header>
|
||||
) -> ClientResult<(NumberFor<B>, <B as Block>::Header)>
|
||||
where
|
||||
B: Block,
|
||||
R: ProvideRuntimeApi<B>,
|
||||
@@ -474,7 +497,7 @@ where
|
||||
"🥩 BEEFY pallet available: block {:?} beefy genesis {:?}",
|
||||
notif.header.number(), start
|
||||
);
|
||||
return Ok(notif.header)
|
||||
return Ok((start, notif.header))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -488,17 +511,6 @@ where
|
||||
Err(ClientError::Backend(err_msg))
|
||||
}
|
||||
|
||||
fn genesis_set_sanity_check(
|
||||
active: ValidatorSet<AuthorityId>,
|
||||
) -> ClientResult<ValidatorSet<AuthorityId>> {
|
||||
if active.id() == GENESIS_AUTHORITY_SET_ID {
|
||||
Ok(active)
|
||||
} else {
|
||||
error!(target: LOG_TARGET, "🥩 Unexpected ID for genesis validator set {:?}.", active);
|
||||
Err(ClientError::Backend("BEEFY Genesis sanity check failed.".into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_validator_set<B, R>(
|
||||
runtime: &R,
|
||||
at_hash: B::Hash,
|
||||
|
||||
Reference in New Issue
Block a user