mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-29 17:15:43 +00:00
8f1b5b840d
The finality-grandpa module needs two sets of functionalities from the network: 1. Everything gossip related, e.g. event_stream, write_notification, ... 2. The ability to set a fork sync request for a specific block hash. Instead of embedding (2) inside of (1) this patch extracts (2) from (1) having finality-grandpa depend on a `Network` that fulfills the `network_gossip::Network` trait and that can set block sync requests. On the one hand this improves the overall structure splitting things that don't logically belong together. On the other hand it does reintroduce a lot of trait bounds within finality-grandpa.
379 lines
11 KiB
Rust
379 lines
11 KiB
Rust
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Substrate.
|
|
|
|
// Substrate is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Substrate is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use futures::prelude::*;
|
|
use futures::{future, sync::mpsc};
|
|
|
|
use finality_grandpa::{
|
|
BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet
|
|
};
|
|
use log::{debug, info, warn};
|
|
|
|
use sp_consensus::SelectChain;
|
|
use sc_client_api::{CallExecutor, backend::Backend};
|
|
use sc_client::Client;
|
|
use sp_runtime::traits::{NumberFor, Block as BlockT};
|
|
use sp_core::{H256, Blake2Hasher};
|
|
|
|
use crate::{
|
|
global_communication, CommandOrError, CommunicationIn, Config, environment,
|
|
LinkHalf, Error, aux_schema::PersistentData, VoterCommand, VoterSetState,
|
|
};
|
|
use crate::authorities::SharedAuthoritySet;
|
|
use crate::communication::{Network as NetworkT, NetworkBridge};
|
|
use crate::consensus_changes::SharedConsensusChanges;
|
|
use sp_finality_grandpa::AuthorityId;
|
|
|
|
struct ObserverChain<'a, Block: BlockT, B, E, RA>(&'a Client<B, E, Block, RA>);
|
|
|
|
impl<'a, Block: BlockT<Hash=H256>, B, E, RA> finality_grandpa::Chain<Block::Hash, NumberFor<Block>>
|
|
for ObserverChain<'a, Block, B, E, RA> where
|
|
B: Backend<Block, Blake2Hasher>,
|
|
E: CallExecutor<Block, Blake2Hasher>,
|
|
NumberFor<Block>: BlockNumberOps,
|
|
{
|
|
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
|
|
environment::ancestry(&self.0, base, block)
|
|
}
|
|
|
|
fn best_chain_containing(&self, _block: Block::Hash) -> Option<(Block::Hash, NumberFor<Block>)> {
|
|
// only used by voter
|
|
None
|
|
}
|
|
}
|
|
|
|
fn grandpa_observer<B, E, Block: BlockT<Hash=H256>, RA, S, F>(
|
|
client: &Arc<Client<B, E, Block, RA>>,
|
|
authority_set: &SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
|
consensus_changes: &SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
|
voters: &Arc<VoterSet<AuthorityId>>,
|
|
last_finalized_number: NumberFor<Block>,
|
|
commits: S,
|
|
note_round: F,
|
|
) -> impl Future<Item=(), Error=CommandOrError<H256, NumberFor<Block>>> where
|
|
NumberFor<Block>: BlockNumberOps,
|
|
B: Backend<Block, Blake2Hasher>,
|
|
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
|
|
RA: Send + Sync,
|
|
S: Stream<
|
|
Item = CommunicationIn<Block>,
|
|
Error = CommandOrError<Block::Hash, NumberFor<Block>>,
|
|
>,
|
|
F: Fn(u64),
|
|
{
|
|
let authority_set = authority_set.clone();
|
|
let consensus_changes = consensus_changes.clone();
|
|
let client = client.clone();
|
|
let voters = voters.clone();
|
|
|
|
let observer = commits.fold(last_finalized_number, move |last_finalized_number, global| {
|
|
let (round, commit, callback) = match global {
|
|
voter::CommunicationIn::Commit(round, commit, callback) => {
|
|
let commit = finality_grandpa::Commit::from(commit);
|
|
(round, commit, callback)
|
|
},
|
|
voter::CommunicationIn::CatchUp(..) => {
|
|
// ignore catch up messages
|
|
return future::ok(last_finalized_number);
|
|
},
|
|
};
|
|
|
|
// if the commit we've received targets a block lower or equal to the last
|
|
// finalized, ignore it and continue with the current state
|
|
if commit.target_number <= last_finalized_number {
|
|
return future::ok(last_finalized_number);
|
|
}
|
|
|
|
let validation_result = match finality_grandpa::validate_commit(
|
|
&commit,
|
|
&voters,
|
|
&ObserverChain(&*client),
|
|
) {
|
|
Ok(r) => r,
|
|
Err(e) => return future::err(e.into()),
|
|
};
|
|
|
|
if let Some(_) = validation_result.ghost() {
|
|
let finalized_hash = commit.target_hash;
|
|
let finalized_number = commit.target_number;
|
|
|
|
// commit is valid, finalize the block it targets
|
|
match environment::finalize_block(
|
|
&client,
|
|
&authority_set,
|
|
&consensus_changes,
|
|
None,
|
|
finalized_hash,
|
|
finalized_number,
|
|
(round, commit).into(),
|
|
) {
|
|
Ok(_) => {},
|
|
Err(e) => return future::err(e),
|
|
};
|
|
|
|
// note that we've observed completion of this round through the commit,
|
|
// and that implies that the next round has started.
|
|
note_round(round + 1);
|
|
|
|
finality_grandpa::process_commit_validation_result(validation_result, callback);
|
|
|
|
// proceed processing with new finalized block number
|
|
future::ok(finalized_number)
|
|
} else {
|
|
debug!(target: "afg", "Received invalid commit: ({:?}, {:?})", round, commit);
|
|
|
|
finality_grandpa::process_commit_validation_result(validation_result, callback);
|
|
|
|
// commit is invalid, continue processing commits with the current state
|
|
future::ok(last_finalized_number)
|
|
}
|
|
});
|
|
|
|
observer.map(|_| ())
|
|
}
|
|
|
|
/// Run a GRANDPA observer as a task, the observer will finalize blocks only by
|
|
/// listening for and validating GRANDPA commits instead of following the full
|
|
/// protocol. Provide configuration and a link to a block import worker that has
|
|
/// already been instantiated with `block_import`.
|
|
pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA, SC, Sp>(
|
|
config: Config,
|
|
link: LinkHalf<B, E, Block, RA, SC>,
|
|
network: N,
|
|
on_exit: impl futures03::Future<Output=()> + Clone + Send + Unpin + 'static,
|
|
executor: Sp,
|
|
) -> ::sp_blockchain::Result<impl Future<Item=(),Error=()> + Send + 'static> where
|
|
B: Backend<Block, Blake2Hasher> + 'static,
|
|
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
|
|
N: NetworkT<Block> + Send + Clone + 'static,
|
|
SC: SelectChain<Block> + 'static,
|
|
NumberFor<Block>: BlockNumberOps,
|
|
RA: Send + Sync + 'static,
|
|
Sp: futures03::task::Spawn + 'static,
|
|
{
|
|
let LinkHalf {
|
|
client,
|
|
select_chain: _,
|
|
persistent_data,
|
|
voter_commands_rx,
|
|
} = link;
|
|
|
|
let network = NetworkBridge::new(
|
|
network,
|
|
config.clone(),
|
|
persistent_data.set_state.clone(),
|
|
&executor,
|
|
on_exit.clone(),
|
|
);
|
|
|
|
let observer_work = ObserverWork::new(
|
|
client,
|
|
network,
|
|
persistent_data,
|
|
config.keystore.clone(),
|
|
voter_commands_rx
|
|
);
|
|
|
|
let observer_work = observer_work
|
|
.map(|_| ())
|
|
.map_err(|e| {
|
|
warn!("GRANDPA Observer failed: {:?}", e);
|
|
});
|
|
|
|
use futures03::{FutureExt, TryFutureExt};
|
|
|
|
Ok(observer_work.select(on_exit.map(Ok).compat()).map(|_| ()).map_err(|_| ()))
|
|
}
|
|
|
|
/// Future that powers the observer.
|
|
#[must_use]
|
|
struct ObserverWork<B: BlockT<Hash=H256>, N: NetworkT<B>, E, Backend, RA> {
|
|
observer: Box<dyn Future<Item = (), Error = CommandOrError<B::Hash, NumberFor<B>>> + Send>,
|
|
client: Arc<Client<Backend, E, B, RA>>,
|
|
network: NetworkBridge<B, N>,
|
|
persistent_data: PersistentData<B>,
|
|
keystore: Option<sc_keystore::KeyStorePtr>,
|
|
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
|
|
}
|
|
|
|
impl<B, N, E, Bk, RA> ObserverWork<B, N, E, Bk, RA>
|
|
where
|
|
B: BlockT<Hash=H256>,
|
|
N: NetworkT<B>,
|
|
NumberFor<B>: BlockNumberOps,
|
|
RA: 'static + Send + Sync,
|
|
E: CallExecutor<B, Blake2Hasher> + Send + Sync + 'static,
|
|
Bk: Backend<B, Blake2Hasher> + 'static,
|
|
{
|
|
fn new(
|
|
client: Arc<Client<Bk, E, B, RA>>,
|
|
network: NetworkBridge<B, N>,
|
|
persistent_data: PersistentData<B>,
|
|
keystore: Option<sc_keystore::KeyStorePtr>,
|
|
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
|
|
) -> Self {
|
|
|
|
let mut work = ObserverWork {
|
|
// `observer` is set to a temporary value and replaced below when
|
|
// calling `rebuild_observer`.
|
|
observer: Box::new(futures::empty()) as Box<_>,
|
|
client,
|
|
network,
|
|
persistent_data,
|
|
keystore,
|
|
voter_commands_rx,
|
|
};
|
|
work.rebuild_observer();
|
|
work
|
|
}
|
|
|
|
/// Rebuilds the `self.observer` field using the current authority set
|
|
/// state. This method should be called when we know that the authority set
|
|
/// has changed (e.g. as signalled by a voter command).
|
|
fn rebuild_observer(&mut self) {
|
|
let set_id = self.persistent_data.authority_set.set_id();
|
|
let voters = Arc::new(self.persistent_data.authority_set.current_authorities());
|
|
|
|
// start global communication stream for the current set
|
|
let (global_in, _) = global_communication(
|
|
set_id,
|
|
&voters,
|
|
&self.client,
|
|
&self.network,
|
|
&self.keystore,
|
|
);
|
|
|
|
let last_finalized_number = self.client.info().chain.finalized_number;
|
|
|
|
// NOTE: since we are not using `round_communication` we have to
|
|
// manually note the round with the gossip validator, otherwise we won't
|
|
// relay round messages. we want all full nodes to contribute to vote
|
|
// availability.
|
|
let note_round = {
|
|
let network = self.network.clone();
|
|
let voters = voters.clone();
|
|
|
|
move |round| network.note_round(
|
|
crate::communication::Round(round),
|
|
crate::communication::SetId(set_id),
|
|
&*voters,
|
|
)
|
|
};
|
|
|
|
// create observer for the current set
|
|
let observer = grandpa_observer(
|
|
&self.client,
|
|
&self.persistent_data.authority_set,
|
|
&self.persistent_data.consensus_changes,
|
|
&voters,
|
|
last_finalized_number,
|
|
global_in,
|
|
note_round,
|
|
);
|
|
|
|
self.observer = Box::new(observer);
|
|
}
|
|
|
|
fn handle_voter_command(
|
|
&mut self,
|
|
command: VoterCommand<B::Hash, NumberFor<B>>,
|
|
) -> Result<(), Error> {
|
|
// the observer doesn't use the voter set state, but we need to
|
|
// update it on-disk in case we restart as validator in the future.
|
|
self.persistent_data.set_state = match command {
|
|
VoterCommand::Pause(reason) => {
|
|
info!(target: "afg", "Pausing old validator set: {}", reason);
|
|
|
|
let completed_rounds = self.persistent_data.set_state.read().completed_rounds();
|
|
let set_state = VoterSetState::Paused { completed_rounds };
|
|
|
|
crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?;
|
|
|
|
set_state
|
|
},
|
|
VoterCommand::ChangeAuthorities(new) => {
|
|
// start the new authority set using the block where the
|
|
// set changed (not where the signal happened!) as the base.
|
|
let set_state = VoterSetState::live(
|
|
new.set_id,
|
|
&*self.persistent_data.authority_set.inner().read(),
|
|
(new.canon_hash, new.canon_number),
|
|
);
|
|
|
|
crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?;
|
|
|
|
set_state
|
|
},
|
|
}.into();
|
|
|
|
self.rebuild_observer();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<B, N, E, Bk, RA> Future for ObserverWork<B, N, E, Bk, RA>
|
|
where
|
|
B: BlockT<Hash=H256>,
|
|
N: NetworkT<B>,
|
|
NumberFor<B>: BlockNumberOps,
|
|
RA: 'static + Send + Sync,
|
|
E: CallExecutor<B, Blake2Hasher> + Send + Sync + 'static,
|
|
Bk: Backend<B, Blake2Hasher> + 'static,
|
|
{
|
|
type Item = ();
|
|
type Error = Error;
|
|
|
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
|
match self.observer.poll() {
|
|
Ok(Async::NotReady) => {}
|
|
Ok(Async::Ready(())) => {
|
|
// observer commit stream doesn't conclude naturally; this could reasonably be an error.
|
|
return Ok(Async::Ready(()))
|
|
}
|
|
Err(CommandOrError::Error(e)) => {
|
|
// return inner observer error
|
|
return Err(e)
|
|
}
|
|
Err(CommandOrError::VoterCommand(command)) => {
|
|
// some command issued internally
|
|
self.handle_voter_command(command)?;
|
|
futures::task::current().notify();
|
|
}
|
|
}
|
|
|
|
match self.voter_commands_rx.poll() {
|
|
Ok(Async::NotReady) => {}
|
|
Err(_) => {
|
|
// the `voter_commands_rx` stream should not fail.
|
|
return Ok(Async::Ready(()))
|
|
}
|
|
Ok(Async::Ready(None)) => {
|
|
// the `voter_commands_rx` stream should never conclude since it's never closed.
|
|
return Ok(Async::Ready(()))
|
|
}
|
|
Ok(Async::Ready(Some(command))) => {
|
|
// some command issued externally
|
|
self.handle_voter_command(command)?;
|
|
futures::task::current().notify();
|
|
}
|
|
}
|
|
|
|
Ok(Async::NotReady)
|
|
}
|
|
}
|