Expose GRANDPA round state through RPC (#5375)

* grandpa: wire up basic RPC call

* grandpa: make it compile against GRANDPA with expose round state

* grandpa: use shared voter state to expose RPC endpoint

* grandpa: restructure into nested structs

* grandpa: return background rounds too

* grandpa: return error when endpoint not ready

* grandpa: collect grandpa rpc deps

* grandpa: decide to use concrete AuthorityId in finality-grandpa-rpc

* grandpa: remove unncessary type annotation

* grandpa: move error code to const

* grandpa: remove unnecessary WIP comment

* grandpa: remove Id type parameter for SharedVoterState

* grandpa: update tests to add shared_voter_state in parameters

* grandpa: remove old deprecated test

* grandpa: fix getting the correct set_id

* grandpa: make SharedVoterState a struct

* grandpa: wrap shared_voter_state in rpc_setup

* grandpa: replace spaces with tabs

* grandpa: limit RwLock write attempt to 1 sec

* grandpa: add missing doc comments and remove some pub

* Apply suggestions from code review

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-Authored-By: Hernando Castano <HCastano@users.noreply.github.com>

* grandpa: update function name call after change in finality-grandpa

* grandpa: group pub use and only export voter::report

* grandpa: add missing docs

* grandpa: extract out structs used for json serialization

* grandpa: stick to u32 for fields intended for js

* grandpa: move Error type to its own file

* grandpa: group pub use better

* Apply code review suggestion

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* grandpa: use correct version of finality-granpda in rpc crate

* grandpa: add back basic rpc unit test

* grandpa: replace SharedVoterState::new() with empty()

* node: cleanup grandpa::SharedVoterState usage in macro

* grandpa: remove VoterState error variant

* grandpa: enable missing futures compat feature

* grandpa: fix typo in error variant

* grandpa: remove test_utils

* grandpa: allow mocking rpc handler components

* grandpa: rename serialized to report in rpc module

* grandpa: add proper test for RPC

* grandpa: update to finality-grandpa v0.12.1

Co-authored-by: André Silva <andre.beat@gmail.com>
Co-authored-by: Demi Obenour <demi@parity.io>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Jon Häggblad
2020-05-04 21:37:22 +02:00
committed by GitHub
parent 8df33e50af
commit 1f7f8abb33
15 changed files with 552 additions and 19 deletions
@@ -39,7 +39,7 @@ pub enum Error<E> {
}
/// A shared authority set.
pub(crate) struct SharedAuthoritySet<H, N> {
pub struct SharedAuthoritySet<H, N> {
inner: Arc<RwLock<AuthoritySet<H, N>>>,
}
@@ -67,12 +67,12 @@ where N: Add<Output=N> + Ord + Clone + Debug,
}
/// Get the current set ID. This is incremented every time the set changes.
pub(crate) fn set_id(&self) -> u64 {
pub fn set_id(&self) -> u64 {
self.inner.read().set_id
}
/// Get the current authorities and their weights (for the current set ID).
pub(crate) fn current_authorities(&self) -> VoterSet<AuthorityId> {
pub fn current_authorities(&self) -> VoterSet<AuthorityId> {
VoterSet::new(self.inner.read().current_authorities.iter().cloned()).expect(
"current_authorities is non-empty and weights are non-zero; \
constructor and all mutating operations on `AuthoritySet` ensure this; \
+66 -1
View File
@@ -52,6 +52,8 @@
//! or prune any signaled changes based on whether the signaling block is
//! included in the newly-finalized chain.
#![warn(missing_docs)]
use futures::prelude::*;
use futures::StreamExt;
use log::{debug, info};
@@ -72,6 +74,7 @@ use sp_core::Pair;
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
use sc_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG};
use serde_json;
use parking_lot::RwLock;
use sp_finality_tracker;
@@ -114,12 +117,14 @@ mod observer;
mod until_imported;
mod voting_rule;
pub use authorities::SharedAuthoritySet;
pub use finality_proof::{FinalityProofProvider, StorageAndProofProvider};
pub use justification::GrandpaJustification;
pub use light_import::light_block_import;
pub use voting_rule::{
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
};
pub use finality_grandpa::voter::report;
use aux_schema::PersistentData;
use environment::{Environment, VoterSetState};
@@ -203,7 +208,44 @@ type CommunicationOutH<Block, H> = finality_grandpa::voter::CommunicationOut<
AuthorityId,
>;
/// Configuration for the GRANDPA service.
/// Shared voter state for querying.
pub struct SharedVoterState {
inner: Arc<RwLock<Option<Box<dyn voter::VoterState<AuthorityId> + Sync + Send>>>>,
}
impl SharedVoterState {
/// Create a new empty `SharedVoterState` instance.
pub fn empty() -> Self {
Self {
inner: Arc::new(RwLock::new(None)),
}
}
fn reset(
&self,
voter_state: Box<dyn voter::VoterState<AuthorityId> + Sync + Send>,
) -> Option<()> {
let mut shared_voter_state = self
.inner
.try_write_for(Duration::from_secs(1))?;
*shared_voter_state = Some(voter_state);
Some(())
}
/// Get the inner `VoterState` instance.
pub fn voter_state(&self) -> Option<voter::report::VoterState<AuthorityId>> {
self.inner.read().as_ref().map(|vs| vs.get())
}
}
impl Clone for SharedVoterState {
fn clone(&self) -> Self {
SharedVoterState { inner: self.inner.clone() }
}
}
/// Configuration for the GRANDPA service
#[derive(Clone)]
pub struct Config {
/// The expected duration for a message to be gossiped across the network.
@@ -392,6 +434,7 @@ impl<H, N> fmt::Display for CommandOrError<H, N> {
}
}
/// Link between the block importer and the background voter.
pub struct LinkHalf<Block: BlockT, C, SC> {
client: Arc<C>,
select_chain: SC,
@@ -399,6 +442,13 @@ pub struct LinkHalf<Block: BlockT, C, SC> {
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
}
impl<Block: BlockT, C, SC> LinkHalf<Block, C, SC> {
/// Get the shared authority set.
pub fn shared_authority_set(&self) -> &SharedAuthoritySet<Block::Hash, NumberFor<Block>> {
&self.persistent_data.authority_set
}
}
/// Provider for the Grandpa authority set configured on the genesis block.
pub trait GenesisAuthoritySetProvider<Block: BlockT> {
/// Get the authority set at the genesis block.
@@ -620,6 +670,8 @@ pub struct GrandpaParams<Block: BlockT, C, N, SC, VR> {
pub voting_rule: VR,
/// The prometheus metrics registry.
pub prometheus_registry: Option<prometheus_endpoint::Registry>,
/// The voter state is exposed at an RPC endpoint.
pub shared_voter_state: SharedVoterState,
}
/// Run a GRANDPA voter as a task. Provide configuration and a link to a
@@ -644,6 +696,7 @@ pub fn run_grandpa_voter<Block: BlockT, BE: 'static, C, N, SC, VR>(
telemetry_on_connect,
voting_rule,
prometheus_registry,
shared_voter_state,
} = grandpa_params;
// NOTE: we have recently removed `run_grandpa_observer` from the public
@@ -704,6 +757,7 @@ pub fn run_grandpa_voter<Block: BlockT, BE: 'static, C, N, SC, VR>(
persistent_data,
voter_commands_rx,
prometheus_registry,
shared_voter_state,
);
let voter_work = voter_work
@@ -734,6 +788,7 @@ impl Metrics {
#[must_use]
struct VoterWork<B, Block: BlockT, C, N: NetworkT<Block>, SC, VR> {
voter: Pin<Box<dyn Future<Output = Result<(), CommandOrError<Block::Hash, NumberFor<Block>>>> + Send>>,
shared_voter_state: SharedVoterState,
env: Arc<Environment<B, Block, C, N, SC, VR>>,
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
network: NetworkBridge<Block, N>,
@@ -761,6 +816,7 @@ where
persistent_data: PersistentData<Block>,
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
prometheus_registry: Option<prometheus_endpoint::Registry>,
shared_voter_state: SharedVoterState,
) -> Self {
let metrics = match prometheus_registry.as_ref().map(Metrics::register) {
Some(Ok(metrics)) => Some(metrics),
@@ -791,6 +847,7 @@ where
// `voter` is set to a temporary value and replaced below when
// calling `rebuild_voter`.
voter: Box::pin(future::pending()),
shared_voter_state,
env,
voter_commands_rx,
network,
@@ -858,6 +915,14 @@ where
last_finalized,
);
// Repoint shared_voter_state so that the RPC endpoint can query the state
if let None = self.shared_voter_state.reset(voter.voter_state()) {
info!(target: "afg",
"Timed out trying to update shared GRANDPA voter state. \
RPC endpoints may return stale data."
);
}
self.voter = Box::pin(voter);
},
VoterSetState::Paused { .. } =>
@@ -295,8 +295,6 @@ fn run_to_completion_with<F>(
) -> u64 where
F: FnOnce(Handle) -> Option<Pin<Box<dyn Future<Output = ()>>>>
{
use parking_lot::RwLock;
let mut wait_for = Vec::new();
let highest_finalized = Arc::new(RwLock::new(0));
@@ -354,6 +352,7 @@ fn run_to_completion_with<F>(
telemetry_on_connect: None,
voting_rule: (),
prometheus_registry: None,
shared_voter_state: SharedVoterState::empty(),
};
let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network");
@@ -485,6 +484,7 @@ fn finalize_3_voters_1_full_observer() {
telemetry_on_connect: None,
voting_rule: (),
prometheus_registry: None,
shared_voter_state: SharedVoterState::empty(),
};
voters.push(run_grandpa_voter(grandpa_params).expect("all in order with client and network"));
@@ -648,6 +648,7 @@ fn transition_3_voters_twice_1_full_observer() {
telemetry_on_connect: None,
voting_rule: (),
prometheus_registry: None,
shared_voter_state: SharedVoterState::empty(),
};
let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network");
@@ -1072,6 +1073,7 @@ fn voter_persists_its_votes() {
telemetry_on_connect: None,
voting_rule: VotingRulesBuilder::default().build(),
prometheus_registry: None,
shared_voter_state: SharedVoterState::empty(),
};
let voter = run_grandpa_voter(grandpa_params)
@@ -1417,6 +1419,7 @@ fn voter_catches_up_to_latest_round_when_behind() {
telemetry_on_connect: None,
voting_rule: (),
prometheus_registry: None,
shared_voter_state: SharedVoterState::empty(),
};
Box::pin(run_grandpa_voter(grandpa_params).expect("all in order with client and network"))