mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-02 14:07:23 +00:00
client/beefy: fix on-demand justifications sync for old blocks (#12767)
* client/beefy: fix on-demand justif sync for old blocks When receiving BEEFY justifications for old blocks the state might be pruned for them, in which case justification verification fails because BEEFY validator set cannot be retrieved from runtime state. Fix this by having the voter give the validator set to the `OnDemandJustificationsEngine` as request information. On receiving a BEEFY justification for requested block, the provided validator set will be used to validate the justification. Signed-off-by: acatangiu <adrian@parity.io> * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * impl review suggestions * client/beefy: fail initialization if state unavailable * beefy: remove spammy log Signed-off-by: acatangiu <adrian@parity.io> Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
+52
-64
@@ -18,21 +18,17 @@
|
||||
|
||||
//! Generating request logic for request/response protocol for syncing BEEFY justifications.
|
||||
|
||||
use beefy_primitives::{crypto::AuthorityId, BeefyApi, ValidatorSet};
|
||||
use beefy_primitives::{crypto::AuthorityId, ValidatorSet};
|
||||
use codec::Encode;
|
||||
use futures::channel::{oneshot, oneshot::Canceled};
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, warn};
|
||||
use parking_lot::Mutex;
|
||||
use sc_network::{PeerId, ProtocolName};
|
||||
use sc_network_common::{
|
||||
request_responses::{IfDisconnected, RequestFailure},
|
||||
service::NetworkRequest,
|
||||
};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block, NumberFor},
|
||||
};
|
||||
use sp_runtime::traits::{Block, NumberFor};
|
||||
use std::{collections::VecDeque, result::Result, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
@@ -46,14 +42,19 @@ type Response = Result<Vec<u8>, RequestFailure>;
|
||||
/// Used to receive a response from the network.
|
||||
type ResponseReceiver = oneshot::Receiver<Response>;
|
||||
|
||||
enum State<B: Block> {
|
||||
Idle,
|
||||
AwaitingResponse(PeerId, NumberFor<B>, ResponseReceiver),
|
||||
#[derive(Clone, Debug)]
|
||||
struct RequestInfo<B: Block> {
|
||||
block: NumberFor<B>,
|
||||
active_set: ValidatorSet<AuthorityId>,
|
||||
}
|
||||
|
||||
pub struct OnDemandJustificationsEngine<B: Block, R> {
|
||||
enum State<B: Block> {
|
||||
Idle,
|
||||
AwaitingResponse(PeerId, RequestInfo<B>, ResponseReceiver),
|
||||
}
|
||||
|
||||
pub struct OnDemandJustificationsEngine<B: Block> {
|
||||
network: Arc<dyn NetworkRequest + Send + Sync>,
|
||||
runtime: Arc<R>,
|
||||
protocol_name: ProtocolName,
|
||||
|
||||
live_peers: Arc<Mutex<KnownPeers<B>>>,
|
||||
@@ -62,21 +63,14 @@ pub struct OnDemandJustificationsEngine<B: Block, R> {
|
||||
state: State<B>,
|
||||
}
|
||||
|
||||
impl<B, R> OnDemandJustificationsEngine<B, R>
|
||||
where
|
||||
B: Block,
|
||||
R: ProvideRuntimeApi<B>,
|
||||
R::Api: BeefyApi<B>,
|
||||
{
|
||||
impl<B: Block> OnDemandJustificationsEngine<B> {
|
||||
pub fn new(
|
||||
network: Arc<dyn NetworkRequest + Send + Sync>,
|
||||
runtime: Arc<R>,
|
||||
protocol_name: ProtocolName,
|
||||
live_peers: Arc<Mutex<KnownPeers<B>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
network,
|
||||
runtime,
|
||||
protocol_name,
|
||||
live_peers,
|
||||
peers_cache: VecDeque::new(),
|
||||
@@ -100,10 +94,15 @@ where
|
||||
None
|
||||
}
|
||||
|
||||
fn request_from_peer(&mut self, peer: PeerId, block: NumberFor<B>) {
|
||||
debug!(target: "beefy::sync", "🥩 requesting justif #{:?} from peer {:?}", block, peer);
|
||||
fn request_from_peer(&mut self, peer: PeerId, req_info: RequestInfo<B>) {
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 requesting justif #{:?} from peer {:?}",
|
||||
req_info.block,
|
||||
peer,
|
||||
);
|
||||
|
||||
let payload = JustificationRequest::<B> { begin: block }.encode();
|
||||
let payload = JustificationRequest::<B> { begin: req_info.block }.encode();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -115,11 +114,13 @@ where
|
||||
IfDisconnected::ImmediateError,
|
||||
);
|
||||
|
||||
self.state = State::AwaitingResponse(peer, block, rx);
|
||||
self.state = State::AwaitingResponse(peer, req_info, rx);
|
||||
}
|
||||
|
||||
/// If no other request is in progress, start new justification request for `block`.
|
||||
pub fn request(&mut self, block: NumberFor<B>) {
|
||||
/// Start new justification request for `block`, if no other request is in progress.
|
||||
///
|
||||
/// `active_set` will be used to verify validity of potential responses.
|
||||
pub fn request(&mut self, block: NumberFor<B>, active_set: ValidatorSet<AuthorityId>) {
|
||||
// ignore new requests while there's already one pending
|
||||
if matches!(self.state, State::AwaitingResponse(_, _, _)) {
|
||||
return
|
||||
@@ -129,7 +130,7 @@ where
|
||||
// Start the requests engine - each unsuccessful received response will automatically
|
||||
// trigger a new request to the next peer in the `peers_cache` until there are none left.
|
||||
if let Some(peer) = self.try_next_peer() {
|
||||
self.request_from_peer(peer, block);
|
||||
self.request_from_peer(peer, RequestInfo { block, active_set });
|
||||
} else {
|
||||
debug!(target: "beefy::sync", "🥩 no good peers to request justif #{:?} from", block);
|
||||
}
|
||||
@@ -138,11 +139,10 @@ where
|
||||
/// Cancel any pending request for block numbers smaller or equal to `block`.
|
||||
pub fn cancel_requests_older_than(&mut self, block: NumberFor<B>) {
|
||||
match &self.state {
|
||||
State::AwaitingResponse(_, number, _) if *number <= block => {
|
||||
State::AwaitingResponse(_, req_info, _) if req_info.block <= block => {
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 cancel pending request for justification #{:?}",
|
||||
number
|
||||
target: "beefy::sync", "🥩 cancel pending request for justification #{:?}",
|
||||
req_info.block
|
||||
);
|
||||
self.state = State::Idle;
|
||||
},
|
||||
@@ -153,8 +153,7 @@ where
|
||||
fn process_response(
|
||||
&mut self,
|
||||
peer: PeerId,
|
||||
block: NumberFor<B>,
|
||||
validator_set: &ValidatorSet<AuthorityId>,
|
||||
req_info: &RequestInfo<B>,
|
||||
response: Result<Response, Canceled>,
|
||||
) -> Result<BeefyVersionedFinalityProof<B>, Error> {
|
||||
response
|
||||
@@ -162,7 +161,7 @@ where
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 for on demand justification #{:?}, peer {:?} hung up: {:?}",
|
||||
block, peer, e
|
||||
req_info.block, peer, e
|
||||
);
|
||||
Error::InvalidResponse
|
||||
})?
|
||||
@@ -170,60 +169,49 @@ where
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 for on demand justification #{:?}, peer {:?} error: {:?}",
|
||||
block, peer, e
|
||||
req_info.block, peer, e
|
||||
);
|
||||
Error::InvalidResponse
|
||||
})
|
||||
.and_then(|encoded| {
|
||||
decode_and_verify_finality_proof::<B>(&encoded[..], block, &validator_set).map_err(
|
||||
|e| {
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}",
|
||||
block, peer, e
|
||||
);
|
||||
Error::InvalidResponse
|
||||
},
|
||||
decode_and_verify_finality_proof::<B>(
|
||||
&encoded[..],
|
||||
req_info.block,
|
||||
&req_info.active_set,
|
||||
)
|
||||
.map_err(|e| {
|
||||
debug!(
|
||||
target: "beefy::sync",
|
||||
"🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}",
|
||||
req_info.block, peer, e
|
||||
);
|
||||
Error::InvalidResponse
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Option<BeefyVersionedFinalityProof<B>> {
|
||||
let (peer, block, resp) = match &mut self.state {
|
||||
let (peer, req_info, resp) = match &mut self.state {
|
||||
State::Idle => {
|
||||
futures::pending!();
|
||||
// Doesn't happen as 'futures::pending!()' is an 'await' barrier that never passes.
|
||||
return None
|
||||
},
|
||||
State::AwaitingResponse(peer, block, receiver) => {
|
||||
State::AwaitingResponse(peer, req_info, receiver) => {
|
||||
let resp = receiver.await;
|
||||
(*peer, *block, resp)
|
||||
(*peer, req_info.clone(), resp)
|
||||
},
|
||||
};
|
||||
// We received the awaited response. Our 'receiver' will never generate any other response,
|
||||
// meaning we're done with current state. Move the engine to `State::Idle`.
|
||||
self.state = State::Idle;
|
||||
|
||||
let block_id = BlockId::number(block);
|
||||
let validator_set = self
|
||||
.runtime
|
||||
.runtime_api()
|
||||
.validator_set(&block_id)
|
||||
.map_err(|e| {
|
||||
error!(target: "beefy::sync", "🥩 Runtime API error {:?} in on-demand justif engine.", e);
|
||||
e
|
||||
})
|
||||
.ok()?
|
||||
.or_else(|| {
|
||||
error!(target: "beefy::sync", "🥩 BEEFY pallet not available for block {:?}.", block);
|
||||
None
|
||||
})?;
|
||||
|
||||
self.process_response(peer, block, &validator_set, resp)
|
||||
let block = req_info.block;
|
||||
self.process_response(peer, &req_info, resp)
|
||||
.map_err(|_| {
|
||||
// No valid justification received, try next peer in our set.
|
||||
if let Some(peer) = self.try_next_peer() {
|
||||
self.request_from_peer(peer, block);
|
||||
self.request_from_peer(peer, req_info);
|
||||
} else {
|
||||
warn!(target: "beefy::sync", "🥩 ran out of peers to request justif #{:?} from", block);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user