Add Subscription RPC for Grandpa Finality (#5732)

* Rough skeleton for what I think the RPC should look like

* Create channel for sending justifications

Sends finalized header and justification from Grandpa to the
client. This lays the groundwork for hooking into the RPC module.

* WIP: Add subscribers for justifications to Grandpa

Adds the Sender end of a channel into Grandpa, through which notifications
about block finality events can be sent.

* WIP: Add a struct for managing subscriptions

Slightly different approach from the last commit, but same
basic idea. Still a rough sketch, very much doesn't compile yet.

* Make naming more clear and lock data in Arc

* Rough idea of what RPC would look like

* Remove code from previous approach

* Missed some things

* Update client/rpc-api/src/chain/mod.rs

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

* Update client/rpc-api/src/chain/mod.rs

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

* Split justification subscription into sender and receiver halves

* Replace RwLock with a Mutex

* Add sample usage from the Service's point of view

* Remove code that referred to "chain_" RPC

* Use the Justification sender/receivers from Grandpa LinkHalf

* Add some PubSub boilerplate

* Add guiding comments

* TMP: comment out to fix compilation

* Return MetaIoHandler from PubSubHandler in create_full

* Uncomment pubsub methods in rpc handler (fails to build)

* node/rpc: make Metadata concrete in create_full to fix compilation

* node: pass in SubscriptionManger to grandpa rpc handler

* grandpa-rpc: use SubscriptionManger to add subscriber

* grandpa-rpc: attempt at setting up the justification stream (fails to build)

* grandpa-rpc: fix compilation of connecting stream to sink

* grandpa-rpc: implement unsubscribe

* grandpa-rpc: update older tests

* grandpa-rpc: add full prefix to avoid confusing rust-analyzer

* grandpa-rpc: add test for pubsub not available

* grandpa-rpc: tidy up leftover code

* grandpa-rpc: add test for sub and unsub of justifications

* grandpa-rpc: minor stylistic changes

* grandpa-rpc: split unit test

* grandpa-rpc: minor stylistic changes in test

* grandpa-rpc: skip returning future when cancelling

* grandpa-rpc: reuse testing executor from sc-rpc

* grandpa-rpc: don't need to use PubSubHandler in tests

* node-rpc: use MetaIoHandler rather than PubSubHandler

* grandpa: log if getting header failed

* grandpa: move justification channel creation into factory function

* grandpa: make the justification sender optional

* grandpa: fix compilation warnings

* grandpa: move justification notification types to new file

* grandpa-rpc: move JustificationNotification to grandpa-rpc

* grandpa-rpc: move JustificationNotification to its own file

* grandpa: rename justification channel pairs

* grandpa: rename notifier types

* grandpa: pass justification as GrandpaJustification to the rpc module

* Move Metadata to sc-rpc-api

* grandpa-rpc: remove unsed error code

* grandpa: fix bug for checking if channel is closed before sendind

* grandpa-rpc: unit test for sending justifications

* grandpa-rpc: update comments for the pubsub test

* grandpa-rpc: update pubsub tests with more steps

* grandpa-rpc: fix pubsub test

* grandpa-rpc: minor indendation

* grandpa-rpc: decode instead of encode in test

* grandpa: fix review comments

* grandpa: remove unused serde dependency

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
This commit is contained in:
Hernando Castano
2020-08-10 06:31:36 -04:00
committed by GitHub
parent a936771c0f
commit 433b7214f5
28 changed files with 589 additions and 94 deletions
@@ -8,16 +8,29 @@ edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
sc-rpc = { version = "2.0.0-rc5", path = "../../rpc" }
sp-runtime = { version = "2.0.0-rc5", path = "../../../primitives/runtime" }
sc-finality-grandpa = { version = "0.8.0-rc5", path = "../" }
finality-grandpa = { version = "0.12.3", features = ["derive-codec"] }
jsonrpc-core = "14.2.0"
jsonrpc-core-client = "14.2.0"
jsonrpc-derive = "14.2.1"
jsonrpc-pubsub = "14.2.0"
futures = { version = "0.3.4", features = ["compat"] }
serde = { version = "1.0.105", features = ["derive"] }
serde_json = "1.0.50"
log = "0.4.8"
derive_more = "0.99.2"
parity-scale-codec = { version = "1.3.0", features = ["derive"] }
[dev-dependencies]
sc-block-builder = { version = "0.8.0-rc5", path = "../../block-builder" }
sc-network-test = { version = "0.8.0-rc5", path = "../../network/test" }
sc-rpc = { version = "2.0.0-rc5", path = "../../rpc", features = ["test-helpers"] }
sp-blockchain = { version = "2.0.0-rc5", path = "../../../primitives/blockchain" }
sp-consensus = { version = "0.8.0-rc5", path = "../../../primitives/consensus/common" }
sp-core = { version = "2.0.0-rc5", path = "../../../primitives/core" }
sp-finality-grandpa = { version = "2.0.0-rc5", path = "../../../primitives/finality-grandpa" }
sp-keyring = { version = "2.0.0-rc5", path = "../../../primitives/keyring" }
substrate-test-runtime-client = { version = "2.0.0-rc5", path = "../../../test-utils/runtime/client" }
lazy_static = "1.4"
+279 -20
View File
@@ -19,13 +19,25 @@
//! RPC API for GRANDPA.
#![warn(missing_docs)]
use futures::{FutureExt, TryFutureExt};
use futures::{FutureExt, TryFutureExt, TryStreamExt, StreamExt};
use log::warn;
use jsonrpc_derive::rpc;
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, manager::SubscriptionManager};
use jsonrpc_core::futures::{
sink::Sink as Sink01,
stream::Stream as Stream01,
future::Future as Future01,
};
mod error;
mod notification;
mod report;
use sc_finality_grandpa::GrandpaJustificationStream;
use sp_runtime::traits::Block as BlockT;
use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
use notification::JustificationNotification;
/// Returned when Grandpa RPC endpoint is not ready.
pub const NOT_READY_ERROR_CODE: i64 = 1;
@@ -35,48 +47,128 @@ type FutureResult<T> =
/// Provides RPC methods for interacting with GRANDPA.
#[rpc]
pub trait GrandpaApi {
pub trait GrandpaApi<Notification> {
/// RPC Metadata
type Metadata;
/// Returns the state of the current best round state as well as the
/// ongoing background rounds.
#[rpc(name = "grandpa_roundState")]
fn round_state(&self) -> FutureResult<ReportedRoundStates>;
/// Returns the block most recently finalized by Grandpa, alongside
/// side its justification.
#[pubsub(
subscription = "grandpa_justifications",
subscribe,
name = "grandpa_subscribeJustifications"
)]
fn subscribe_justifications(
&self,
metadata: Self::Metadata,
subscriber: Subscriber<Notification>
);
/// Unsubscribe from receiving notifications about recently finalized blocks.
#[pubsub(
subscription = "grandpa_justifications",
unsubscribe,
name = "grandpa_unsubscribeJustifications"
)]
fn unsubscribe_justifications(
&self,
metadata: Option<Self::Metadata>,
id: SubscriptionId
) -> jsonrpc_core::Result<bool>;
}
/// Implements the GrandpaApi RPC trait for interacting with GRANDPA.
pub struct GrandpaRpcHandler<AuthoritySet, VoterState> {
pub struct GrandpaRpcHandler<AuthoritySet, VoterState, Block: BlockT> {
authority_set: AuthoritySet,
voter_state: VoterState,
justification_stream: GrandpaJustificationStream<Block>,
manager: SubscriptionManager,
}
impl<AuthoritySet, VoterState> GrandpaRpcHandler<AuthoritySet, VoterState> {
/// Creates a new GrandpaRpcHander instance.
pub fn new(authority_set: AuthoritySet, voter_state: VoterState) -> Self {
impl<AuthoritySet, VoterState, Block: BlockT> GrandpaRpcHandler<AuthoritySet, VoterState, Block> {
/// Creates a new GrandpaRpcHandler instance.
pub fn new(
authority_set: AuthoritySet,
voter_state: VoterState,
justification_stream: GrandpaJustificationStream<Block>,
manager: SubscriptionManager,
) -> Self {
Self {
authority_set,
voter_state,
justification_stream,
manager,
}
}
}
impl<AuthoritySet, VoterState> GrandpaApi for GrandpaRpcHandler<AuthoritySet, VoterState>
impl<AuthoritySet, VoterState, Block> GrandpaApi<JustificationNotification>
for GrandpaRpcHandler<AuthoritySet, VoterState, Block>
where
VoterState: ReportVoterState + Send + Sync + 'static,
AuthoritySet: ReportAuthoritySet + Send + Sync + 'static,
Block: BlockT,
{
type Metadata = sc_rpc::Metadata;
fn round_state(&self) -> FutureResult<ReportedRoundStates> {
let round_states = ReportedRoundStates::from(&self.authority_set, &self.voter_state);
let future = async move { round_states }.boxed();
Box::new(future.map_err(jsonrpc_core::Error::from).compat())
}
fn subscribe_justifications(
&self,
_metadata: Self::Metadata,
subscriber: Subscriber<JustificationNotification>
) {
let stream = self.justification_stream.subscribe()
.map(|x| Ok::<_,()>(JustificationNotification::from(x)))
.map_err(|e| warn!("Notification stream error: {:?}", e))
.compat();
self.manager.add(subscriber, |sink| {
let stream = stream.map(|res| Ok(res));
sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e))
.send_all(stream)
.map(|_| ())
});
}
fn unsubscribe_justifications(
&self,
_metadata: Option<Self::Metadata>,
id: SubscriptionId
) -> jsonrpc_core::Result<bool> {
Ok(self.manager.cancel(id))
}
}
#[cfg(test)]
mod tests {
use super::*;
use jsonrpc_core::IoHandler;
use sc_finality_grandpa::{report, AuthorityId};
use std::{collections::HashSet, convert::TryInto, sync::Arc};
use jsonrpc_core::{Notification, Output, types::Params};
use parity_scale_codec::Decode;
use sc_block_builder::BlockBuilder;
use sc_finality_grandpa::{report, AuthorityId, GrandpaJustificationSender, GrandpaJustification};
use sp_blockchain::HeaderBackend;
use sp_consensus::RecordProof;
use sp_core::crypto::Public;
use std::{collections::HashSet, convert::TryInto};
use sp_keyring::Ed25519Keyring;
use sp_runtime::traits::Header as HeaderT;
use substrate_test_runtime_client::{
runtime::Block,
DefaultTestClientBuilderExt,
TestClientBuilderExt,
TestClientBuilder,
};
struct TestAuthoritySet;
struct TestVoterState;
@@ -106,7 +198,7 @@ mod tests {
let voter_id_1 = AuthorityId::from_slice(&[1; 32]);
let voters_best: HashSet<_> = vec![voter_id_1].into_iter().collect();
let best_round_state = report::RoundState {
let best_round_state = sc_finality_grandpa::report::RoundState {
total_weight: 100_u64.try_into().unwrap(),
threshold_weight: 67_u64.try_into().unwrap(),
prevote_current_weight: 50.into(),
@@ -115,7 +207,7 @@ mod tests {
precommit_ids: HashSet::new(),
};
let past_round_state = report::RoundState {
let past_round_state = sc_finality_grandpa::report::RoundState {
total_weight: 100_u64.try_into().unwrap(),
threshold_weight: 67_u64.try_into().unwrap(),
prevote_current_weight: 100.into(),
@@ -133,23 +225,42 @@ mod tests {
}
}
fn setup_io_handler<VoterState>(voter_state: VoterState) -> (
jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>,
GrandpaJustificationSender<Block>,
) where
VoterState: ReportVoterState + Send + Sync + 'static,
{
let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
let manager = SubscriptionManager::new(Arc::new(sc_rpc::testing::TaskExecutor));
let handler = GrandpaRpcHandler::new(
TestAuthoritySet,
voter_state,
justification_stream,
manager,
);
let mut io = jsonrpc_core::MetaIoHandler::default();
io.extend_with(GrandpaApi::to_delegate(handler));
(io, justification_sender)
}
#[test]
fn uninitialized_rpc_handler() {
let handler = GrandpaRpcHandler::new(TestAuthoritySet, EmptyVoterState);
let mut io = IoHandler::new();
io.extend_with(GrandpaApi::to_delegate(handler));
let (io, _) = setup_io_handler(EmptyVoterState);
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":1}"#;
assert_eq!(Some(response.into()), io.handle_request_sync(request));
let meta = sc_rpc::Metadata::default();
assert_eq!(Some(response.into()), io.handle_request_sync(request, meta));
}
#[test]
fn working_rpc_handler() {
let handler = GrandpaRpcHandler::new(TestAuthoritySet, TestVoterState);
let mut io = IoHandler::new();
io.extend_with(GrandpaApi::to_delegate(handler));
let (io, _) = setup_io_handler(TestVoterState);
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":1}"#;
let response = "{\"jsonrpc\":\"2.0\",\"result\":{\
@@ -166,6 +277,154 @@ mod tests {
\"setId\":1\
},\"id\":1}";
assert_eq!(io.handle_request_sync(request), Some(response.into()));
let meta = sc_rpc::Metadata::default();
assert_eq!(io.handle_request_sync(request, meta), Some(response.into()));
}
fn setup_session() -> (sc_rpc::Metadata, jsonrpc_core::futures::sync::mpsc::Receiver<String>) {
let (tx, rx) = jsonrpc_core::futures::sync::mpsc::channel(1);
let meta = sc_rpc::Metadata::new(tx);
(meta, rx)
}
#[test]
fn subscribe_and_unsubscribe_to_justifications() {
let (io, _) = setup_io_handler(TestVoterState);
let (meta, _) = setup_session();
// Subscribe
let sub_request = r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
let resp = io.handle_request_sync(sub_request, meta.clone());
let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
let sub_id = match resp {
Output::Success(success) => success.result,
_ => panic!(),
};
// Unsubscribe
let unsub_req = format!(
"{{\"jsonrpc\":\"2.0\",\"method\":\"grandpa_unsubscribeJustifications\",\"params\":[{}],\"id\":1}}",
sub_id
);
assert_eq!(
io.handle_request_sync(&unsub_req, meta.clone()),
Some(r#"{"jsonrpc":"2.0","result":true,"id":1}"#.into()),
);
// Unsubscribe again and fail
assert_eq!(
io.handle_request_sync(&unsub_req, meta),
Some(r#"{"jsonrpc":"2.0","result":false,"id":1}"#.into()),
);
}
#[test]
fn subscribe_and_unsubscribe_with_wrong_id() {
let (io, _) = setup_io_handler(TestVoterState);
let (meta, _) = setup_session();
// Subscribe
let sub_request = r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
let resp = io.handle_request_sync(sub_request, meta.clone());
let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
assert!(matches!(resp, Output::Success(_)));
// Unsubscribe with wrong ID
assert_eq!(
io.handle_request_sync(
r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#,
meta.clone()
),
Some(r#"{"jsonrpc":"2.0","result":false,"id":1}"#.into())
);
}
fn create_justification() -> GrandpaJustification<Block> {
let peers = &[Ed25519Keyring::Alice];
let builder = TestClientBuilder::new();
let backend = builder.backend();
let client = builder.build();
let client = Arc::new(client);
let built_block = BlockBuilder::new(
&*client,
client.info().best_hash,
client.info().best_number,
RecordProof::Yes,
Default::default(),
&*backend,
).unwrap().build().unwrap();
let block = built_block.block;
let block_hash = block.hash();
let justification = {
let round = 1;
let set_id = 0;
let precommit = finality_grandpa::Precommit {
target_hash: block_hash,
target_number: *block.header.number(),
};
let msg = finality_grandpa::Message::Precommit(precommit.clone());
let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg);
let signature = peers[0].sign(&encoded[..]).into();
let precommit = finality_grandpa::SignedPrecommit {
precommit,
signature,
id: peers[0].public().into(),
};
let commit = finality_grandpa::Commit {
target_hash: block_hash,
target_number: *block.header.number(),
precommits: vec![precommit],
};
GrandpaJustification::from_commit(&client, round, commit).unwrap()
};
justification
}
#[test]
fn subscribe_and_listen_to_one_justification() {
let (io, justification_sender) = setup_io_handler(TestVoterState);
let (meta, receiver) = setup_session();
// Subscribe
let sub_request =
r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
let resp = io.handle_request_sync(sub_request, meta.clone());
let mut resp: serde_json::Value = serde_json::from_str(&resp.unwrap()).unwrap();
let sub_id: String = serde_json::from_value(resp["result"].take()).unwrap();
// Notify with a header and justification
let justification = create_justification();
let _ = justification_sender.notify(justification.clone()).unwrap();
// Inspect what we received
let recv = receiver.take(1).wait().flatten().collect::<Vec<_>>();
let recv: Notification = serde_json::from_str(&recv[0]).unwrap();
let mut json_map = match recv.params {
Params::Map(json_map) => json_map,
_ => panic!(),
};
let recv_sub_id: String =
serde_json::from_value(json_map["subscription"].take()).unwrap();
let recv_justification: Vec<u8> =
serde_json::from_value(json_map["result"].take()).unwrap();
let recv_justification: GrandpaJustification<Block> =
Decode::decode(&mut &recv_justification[..]).unwrap();
assert_eq!(recv.method, "grandpa_justifications");
assert_eq!(recv_sub_id, sub_id);
assert_eq!(recv_justification, justification);
}
}
@@ -0,0 +1,32 @@
// This file is part of Substrate.
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use serde::{Serialize, Deserialize};
use parity_scale_codec::Encode;
use sp_runtime::traits::Block as BlockT;
use sc_finality_grandpa::GrandpaJustification;
/// An encoded justification proving that the given header has been finalized
#[derive(Clone, Serialize, Deserialize)]
pub struct JustificationNotification(Vec<u8>);
impl<Block: BlockT> From<GrandpaJustification<Block>> for JustificationNotification {
fn from(notification: GrandpaJustification<Block>) -> Self {
JustificationNotification(notification.encode())
}
}
@@ -51,6 +51,7 @@ use sp_consensus::SelectChain;
use crate::authorities::{AuthoritySet, SharedAuthoritySet};
use crate::communication::Network as NetworkT;
use crate::consensus_changes::SharedConsensusChanges;
use crate::notification::GrandpaJustificationSender;
use crate::justification::GrandpaJustification;
use crate::until_imported::UntilVoteTargetImported;
use crate::voting_rule::VotingRule;
@@ -390,7 +391,6 @@ impl Metrics {
}
}
/// The environment we run GRANDPA in.
pub(crate) struct Environment<Backend, Block: BlockT, C, N: NetworkT<Block>, SC, VR> {
pub(crate) client: Arc<C>,
@@ -404,6 +404,7 @@ pub(crate) struct Environment<Backend, Block: BlockT, C, N: NetworkT<Block>, SC,
pub(crate) voter_set_state: SharedVoterSetState<Block>,
pub(crate) voting_rule: VR,
pub(crate) metrics: Option<Metrics>,
pub(crate) justification_sender: Option<GrandpaJustificationSender<Block>>,
pub(crate) _phantom: PhantomData<Backend>,
}
@@ -1022,6 +1023,7 @@ where
number,
(round, commit).into(),
false,
&self.justification_sender,
)
}
@@ -1086,6 +1088,7 @@ pub(crate) fn finalize_block<BE, Block, Client>(
number: NumberFor<Block>,
justification_or_commit: JustificationOrCommit<Block>,
initial_sync: bool,
justification_sender: &Option<GrandpaJustificationSender<Block>>,
) -> Result<(), CommandOrError<Block::Hash, NumberFor<Block>>> where
Block: BlockT,
BE: Backend<Block>,
@@ -1097,6 +1100,7 @@ pub(crate) fn finalize_block<BE, Block, Client>(
let mut authority_set = authority_set.inner().write();
let status = client.info();
if number <= status.finalized_number && client.hash(number)? == Some(hash) {
// This can happen after a forced change (triggered by the finality tracker when finality is stalled), since
// the voter will be restarted at the median last finalized block, which can be lower than the local best
@@ -1157,7 +1161,7 @@ pub(crate) fn finalize_block<BE, Block, Client>(
// justifications for transition blocks which will be requested by
// syncing clients.
let justification = match justification_or_commit {
JustificationOrCommit::Justification(justification) => Some(justification.encode()),
JustificationOrCommit::Justification(justification) => Some(justification),
JustificationOrCommit::Commit((round_number, commit)) => {
let mut justification_required =
// justification is always required when block that enacts new authorities
@@ -1184,13 +1188,22 @@ pub(crate) fn finalize_block<BE, Block, Client>(
commit,
)?;
Some(justification.encode())
Some(justification)
} else {
None
}
},
};
// Notify any registered listeners in case we have a justification
if let Some(sender) = justification_sender {
if let Some(ref justification) = justification {
let _ = sender.notify(justification.clone());
}
}
let justification = justification.map(|j| j.encode());
debug!(target: "afg", "Finalizing blocks up to ({:?}, {})", number, hash);
// ideally some handle to a synchronization oracle would be used
@@ -149,7 +149,7 @@ impl<Block: BlockT> AuthoritySetForFinalityChecker<Block> for Arc<dyn FetchCheck
}
/// Finality proof provider for serving network requests.
pub struct FinalityProofProvider<B, Block: BlockT> {
pub struct FinalityProofProvider<B, Block: BlockT> {
backend: Arc<B>,
authority_provider: Arc<dyn AuthoritySetForFinalityProver<Block>>,
}
@@ -44,6 +44,7 @@ use crate::authorities::{AuthoritySet, SharedAuthoritySet, DelayKind, PendingCha
use crate::consensus_changes::SharedConsensusChanges;
use crate::environment::finalize_block;
use crate::justification::GrandpaJustification;
use crate::notification::GrandpaJustificationSender;
use std::marker::PhantomData;
/// A block-import handler for GRANDPA.
@@ -62,6 +63,7 @@ pub struct GrandpaBlockImport<Backend, Block: BlockT, Client, SC> {
send_voter_commands: TracingUnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
authority_set_hard_forks: HashMap<Block::Hash, PendingChange<Block::Hash, NumberFor<Block>>>,
justification_sender: GrandpaJustificationSender<Block>,
_phantom: PhantomData<Backend>,
}
@@ -76,6 +78,7 @@ impl<Backend, Block: BlockT, Client, SC: Clone> Clone for
send_voter_commands: self.send_voter_commands.clone(),
consensus_changes: self.consensus_changes.clone(),
authority_set_hard_forks: self.authority_set_hard_forks.clone(),
justification_sender: self.justification_sender.clone(),
_phantom: PhantomData,
}
}
@@ -560,6 +563,7 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
send_voter_commands: TracingUnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
authority_set_hard_forks: Vec<(SetId, PendingChange<Block::Hash, NumberFor<Block>>)>,
justification_sender: GrandpaJustificationSender<Block>,
) -> GrandpaBlockImport<Backend, Block, Client, SC> {
// check for and apply any forced authority set hard fork that applies
// to the *current* authority set.
@@ -603,6 +607,7 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
send_voter_commands,
consensus_changes,
authority_set_hard_forks,
justification_sender,
_phantom: PhantomData,
}
}
@@ -648,6 +653,7 @@ where
number,
justification.into(),
initial_sync,
&Some(self.justification_sender.clone()),
);
match result {
@@ -37,7 +37,7 @@ use crate::{Commit, Error};
///
/// This is meant to be stored in the db and passed around the network to other
/// nodes, and are used by syncing nodes to prove authority set handoffs.
#[derive(Encode, Decode)]
#[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)]
pub struct GrandpaJustification<Block: BlockT> {
round: u64,
pub(crate) commit: Commit<Block>,
@@ -47,7 +47,7 @@ pub struct GrandpaJustification<Block: BlockT> {
impl<Block: BlockT> GrandpaJustification<Block> {
/// Create a GRANDPA justification from the given commit. This method
/// assumes the commit is valid and well-formed.
pub(crate) fn from_commit<C>(
pub fn from_commit<C>(
client: &Arc<C>,
round: u64,
commit: Commit<Block>,
@@ -119,12 +119,14 @@ mod finality_proof;
mod import;
mod justification;
mod light_import;
mod notification;
mod observer;
mod until_imported;
mod voting_rule;
pub use authorities::SharedAuthoritySet;
pub use finality_proof::{FinalityProofProvider, StorageAndProofProvider};
pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream};
pub use import::GrandpaBlockImport;
pub use justification::GrandpaJustification;
pub use light_import::{light_block_import, GrandpaLightBlockImport};
@@ -448,6 +450,8 @@ pub struct LinkHalf<Block: BlockT, C, SC> {
select_chain: SC,
persistent_data: PersistentData<Block>,
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
justification_sender: GrandpaJustificationSender<Block>,
justification_stream: GrandpaJustificationStream<Block>,
}
impl<Block: BlockT, C, SC> LinkHalf<Block, C, SC> {
@@ -455,6 +459,11 @@ impl<Block: BlockT, C, SC> LinkHalf<Block, C, SC> {
pub fn shared_authority_set(&self) -> &SharedAuthoritySet<Block::Hash, NumberFor<Block>> {
&self.persistent_data.authority_set
}
/// Get the receiving end of justification notifications.
pub fn justification_stream(&self) -> GrandpaJustificationStream<Block> {
self.justification_stream.clone()
}
}
/// Provider for the Grandpa authority set configured on the genesis block.
@@ -553,6 +562,9 @@ where
let (voter_commands_tx, voter_commands_rx) = tracing_unbounded("mpsc_grandpa_voter_command");
let (justification_sender, justification_stream) =
GrandpaJustificationStream::channel();
// create pending change objects with 0 delay and enacted on finality
// (i.e. standard changes) for each authority set hard fork.
let authority_set_hard_forks = authority_set_hard_forks
@@ -579,12 +591,15 @@ where
voter_commands_tx,
persistent_data.consensus_changes.clone(),
authority_set_hard_forks,
justification_sender.clone(),
),
LinkHalf {
client,
select_chain,
persistent_data,
voter_commands_rx,
justification_sender,
justification_stream,
},
))
}
@@ -719,6 +734,8 @@ pub fn run_grandpa_voter<Block: BlockT, BE: 'static, C, N, SC, VR>(
select_chain,
persistent_data,
voter_commands_rx,
justification_sender,
justification_stream: _,
} = link;
let network = NetworkBridge::new(
@@ -767,6 +784,7 @@ pub fn run_grandpa_voter<Block: BlockT, BE: 'static, C, N, SC, VR>(
voter_commands_rx,
prometheus_registry,
shared_voter_state,
justification_sender,
);
let voter_work = voter_work
@@ -827,6 +845,7 @@ where
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
prometheus_registry: Option<prometheus_endpoint::Registry>,
shared_voter_state: SharedVoterState,
justification_sender: GrandpaJustificationSender<Block>,
) -> Self {
let metrics = match prometheus_registry.as_ref().map(Metrics::register) {
Some(Ok(metrics)) => Some(metrics),
@@ -850,6 +869,7 @@ where
consensus_changes: persistent_data.consensus_changes.clone(),
voter_set_state: persistent_data.set_state,
metrics: metrics.as_ref().map(|m| m.environment.clone()),
justification_sender: Some(justification_sender),
_phantom: PhantomData,
});
@@ -988,6 +1008,7 @@ where
network: self.env.network.clone(),
voting_rule: self.env.voting_rule.clone(),
metrics: self.env.metrics.clone(),
justification_sender: self.env.justification_sender.clone(),
_phantom: PhantomData,
});
@@ -0,0 +1,102 @@
// This file is part of Substrate.
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use std::sync::Arc;
use parking_lot::Mutex;
use sp_runtime::traits::Block as BlockT;
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnboundedReceiver};
use crate::justification::GrandpaJustification;
// Stream of justifications returned when subscribing.
type JustificationStream<Block> = TracingUnboundedReceiver<GrandpaJustification<Block>>;
// Sending endpoint for notifying about justifications.
type JustificationSender<Block> = TracingUnboundedSender<GrandpaJustification<Block>>;
// Collection of channel sending endpoints shared with the receiver side so they can register
// themselves.
type SharedJustificationSenders<Block> = Arc<Mutex<Vec<JustificationSender<Block>>>>;
/// The sending half of the Grandpa justification channel(s).
///
/// Used to send notifications about justifications generated
/// at the end of a Grandpa round.
#[derive(Clone)]
pub struct GrandpaJustificationSender<Block: BlockT> {
subscribers: SharedJustificationSenders<Block>
}
impl<Block: BlockT> GrandpaJustificationSender<Block> {
/// The `subscribers` should be shared with a corresponding
/// `GrandpaJustificationStream`.
fn new(subscribers: SharedJustificationSenders<Block>) -> Self {
Self {
subscribers,
}
}
/// Send out a notification to all subscribers that a new justification
/// is available for a block.
pub fn notify(&self, notification: GrandpaJustification<Block>) -> Result<(), ()> {
self.subscribers.lock().retain(|n| {
!n.is_closed() && n.unbounded_send(notification.clone()).is_ok()
});
Ok(())
}
}
/// The receiving half of the Grandpa justification channel.
///
/// Used to receive notifications about justifications generated
/// at the end of a Grandpa round.
/// The `GrandpaJustificationStream` entity stores the `SharedJustificationSenders`
/// so it can be used to add more subscriptions.
#[derive(Clone)]
pub struct GrandpaJustificationStream<Block: BlockT> {
subscribers: SharedJustificationSenders<Block>
}
impl<Block: BlockT> GrandpaJustificationStream<Block> {
/// Creates a new pair of receiver and sender of justification notifications.
pub fn channel() -> (GrandpaJustificationSender<Block>, Self) {
let subscribers = Arc::new(Mutex::new(vec![]));
let receiver = GrandpaJustificationStream::new(subscribers.clone());
let sender = GrandpaJustificationSender::new(subscribers.clone());
(sender, receiver)
}
/// Create a new receiver of justification notifications.
///
/// The `subscribers` should be shared with a corresponding
/// `GrandpaJustificationSender`.
fn new(subscribers: SharedJustificationSenders<Block>) -> Self {
Self {
subscribers,
}
}
/// Subscribe to a channel through which justifications are sent
/// at the end of each Grandpa voting round.
pub fn subscribe(&self) -> JustificationStream<Block> {
let (sender, receiver) = tracing_unbounded("mpsc_justification_notification_stream");
self.subscribers.lock().push(sender);
receiver
}
}
@@ -40,6 +40,7 @@ use crate::{
use crate::authorities::SharedAuthoritySet;
use crate::communication::{Network as NetworkT, NetworkBridge};
use crate::consensus_changes::SharedConsensusChanges;
use crate::notification::GrandpaJustificationSender;
use sp_finality_grandpa::AuthorityId;
use std::marker::{PhantomData, Unpin};
@@ -69,6 +70,7 @@ fn grandpa_observer<BE, Block: BlockT, Client, S, F>(
authority_set: &SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
consensus_changes: &SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
voters: &Arc<VoterSet<AuthorityId>>,
justification_sender: &Option<GrandpaJustificationSender<Block>>,
last_finalized_number: NumberFor<Block>,
commits: S,
note_round: F,
@@ -85,6 +87,7 @@ fn grandpa_observer<BE, Block: BlockT, Client, S, F>(
let consensus_changes = consensus_changes.clone();
let client = client.clone();
let voters = voters.clone();
let justification_sender = justification_sender.clone();
let observer = commits.try_fold(last_finalized_number, move |last_finalized_number, global| {
let (round, commit, callback) = match global {
@@ -127,6 +130,7 @@ fn grandpa_observer<BE, Block: BlockT, Client, S, F>(
finalized_number,
(round, commit).into(),
false,
&justification_sender,
) {
Ok(_) => {},
Err(e) => return future::err(e),
@@ -177,6 +181,7 @@ where
select_chain: _,
persistent_data,
voter_commands_rx,
justification_sender,
..
} = link;
@@ -192,7 +197,8 @@ where
network,
persistent_data,
config.keystore,
voter_commands_rx
voter_commands_rx,
Some(justification_sender),
);
let observer_work = observer_work
@@ -213,6 +219,7 @@ struct ObserverWork<B: BlockT, BE, Client, N: NetworkT<B>> {
persistent_data: PersistentData<B>,
keystore: Option<BareCryptoStorePtr>,
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
justification_sender: Option<GrandpaJustificationSender<B>>,
_phantom: PhantomData<BE>,
}
@@ -230,6 +237,7 @@ where
persistent_data: PersistentData<B>,
keystore: Option<BareCryptoStorePtr>,
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
justification_sender: Option<GrandpaJustificationSender<B>>,
) -> Self {
let mut work = ObserverWork {
@@ -241,6 +249,7 @@ where
persistent_data,
keystore: keystore.clone(),
voter_commands_rx,
justification_sender,
_phantom: PhantomData,
};
work.rebuild_observer();
@@ -287,6 +296,7 @@ where
&self.persistent_data.authority_set,
&self.persistent_data.consensus_changes,
&voters,
&self.justification_sender,
last_finalized_number,
global_in,
note_round,
@@ -422,12 +432,14 @@ mod tests {
).unwrap();
let (_tx, voter_command_rx) = tracing_unbounded("");
let observer = ObserverWork::new(
client,
tester.net_handle.clone(),
persistent_data,
None,
voter_command_rx,
None,
);
// Trigger a reputation change through the gossip validator.
@@ -1567,6 +1567,7 @@ where
network,
voting_rule,
metrics: None,
justification_sender: None,
_phantom: PhantomData,
}
}
+3 -1
View File
@@ -22,10 +22,12 @@
mod errors;
mod helpers;
mod metadata;
mod policy;
pub use jsonrpc_core::IoHandlerExtension as RpcExtension;
pub use helpers::Receiver;
pub use jsonrpc_core::IoHandlerExtension as RpcExtension;
pub use metadata::Metadata;
pub use policy::DenyUnsafe;
pub mod author;
@@ -19,8 +19,8 @@
//! RPC Metadata
use std::sync::Arc;
use jsonrpc_core::futures::sync::mpsc;
use jsonrpc_pubsub::{Session, PubSubMetadata};
use rpc::futures::sync::mpsc;
/// RPC Metadata.
///
@@ -32,7 +32,7 @@ pub struct Metadata {
session: Option<Arc<Session>>,
}
impl rpc::Metadata for Metadata {}
impl jsonrpc_core::Metadata for Metadata {}
impl PubSubMetadata for Metadata {
fn session(&self) -> Option<Arc<Session>> {
self.session.clone()
+4 -1
View File
@@ -37,6 +37,7 @@ sp-transaction-pool = { version = "2.0.0-rc5", path = "../../primitives/transact
sp-blockchain = { version = "2.0.0-rc5", path = "../../primitives/blockchain" }
hash-db = { version = "0.15.2", default-features = false }
parking_lot = "0.10.0"
lazy_static = { version = "1.4.0", optional = true }
[dev-dependencies]
assert_matches = "1.3.0"
@@ -46,4 +47,6 @@ sp-io = { version = "2.0.0-rc5", path = "../../primitives/io" }
substrate-test-runtime-client = { version = "2.0.0-rc5", path = "../../test-utils/runtime/client" }
tokio = "0.1.22"
sc-transaction-pool = { version = "2.0.0-rc5", path = "../transaction-pool" }
lazy_static = "1.4.0"
[features]
test-helpers = ["lazy_static"]
+1 -1
View File
@@ -94,7 +94,7 @@ impl<P, Client> AuthorApi<TxHash<P>, BlockHash<P>> for Author<P, Client>
Client: HeaderBackend<P::Block> + ProvideRuntimeApi<P::Block> + Send + Sync + 'static,
Client::Api: SessionKeys<P::Block, Error = ClientError>,
{
type Metadata = crate::metadata::Metadata;
type Metadata = crate::Metadata;
fn insert_key(
&self,
+7 -7
View File
@@ -106,7 +106,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// All new head subscription
fn subscribe_all_heads(
&self,
_metadata: crate::metadata::Metadata,
_metadata: crate::Metadata,
subscriber: Subscriber<Block::Header>,
) {
subscribe_headers(
@@ -123,7 +123,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// Unsubscribe from all head subscription.
fn unsubscribe_all_heads(
&self,
_metadata: Option<crate::metadata::Metadata>,
_metadata: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions().cancel(id))
@@ -132,7 +132,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// New best head subscription
fn subscribe_new_heads(
&self,
_metadata: crate::metadata::Metadata,
_metadata: crate::Metadata,
subscriber: Subscriber<Block::Header>,
) {
subscribe_headers(
@@ -150,7 +150,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// Unsubscribe from new best head subscription.
fn unsubscribe_new_heads(
&self,
_metadata: Option<crate::metadata::Metadata>,
_metadata: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions().cancel(id))
@@ -159,7 +159,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// Finalized head subscription
fn subscribe_finalized_heads(
&self,
_metadata: crate::metadata::Metadata,
_metadata: crate::Metadata,
subscriber: Subscriber<Block::Header>,
) {
subscribe_headers(
@@ -176,7 +176,7 @@ trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
/// Unsubscribe from finalized head subscription.
fn unsubscribe_finalized_heads(
&self,
_metadata: Option<crate::metadata::Metadata>,
_metadata: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions().cancel(id))
@@ -230,7 +230,7 @@ impl<Block, Client> ChainApi<NumberFor<Block>, Block::Hash, Block::Header, Signe
Block: BlockT + 'static,
Client: HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
{
type Metadata = crate::metadata::Metadata;
type Metadata = crate::Metadata;
fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>> {
self.backend.header(hash)
+4 -6
View File
@@ -27,10 +27,7 @@ use rpc::futures::future::{Executor, ExecuteError, Future};
use sp_core::traits::SpawnNamed;
use std::sync::Arc;
mod metadata;
pub use sc_rpc_api::DenyUnsafe;
pub use self::metadata::Metadata;
pub use sc_rpc_api::{DenyUnsafe, Metadata};
pub use rpc::IoHandlerExtension as RpcExtension;
pub mod author;
@@ -38,8 +35,9 @@ pub mod chain;
pub mod offchain;
pub mod state;
pub mod system;
#[cfg(test)]
mod testing;
#[cfg(any(test, feature = "test-helpers"))]
pub mod testing;
/// Task executor that is being used by RPC subscriptions.
#[derive(Clone)]
+6 -6
View File
@@ -140,21 +140,21 @@ pub trait StateBackend<Block: BlockT, Client>: Send + Sync + 'static
/// New runtime version subscription
fn subscribe_runtime_version(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<RuntimeVersion>,
);
/// Unsubscribe from runtime version subscription
fn unsubscribe_runtime_version(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool>;
/// New storage subscription
fn subscribe_storage(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
keys: Option<Vec<StorageKey>>,
);
@@ -162,7 +162,7 @@ pub trait StateBackend<Block: BlockT, Client>: Send + Sync + 'static
/// Unsubscribe from storage subscription
fn unsubscribe_storage(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool>;
}
@@ -230,7 +230,7 @@ impl<Block, Client> StateApi<Block::Hash> for State<Block, Client>
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
type Metadata = crate::metadata::Metadata;
type Metadata = crate::Metadata;
fn call(&self, method: String, data: Bytes, block: Option<Block::Hash>) -> FutureResult<Bytes> {
self.backend.call(block, method, data)
@@ -390,7 +390,7 @@ impl<Block, Client> ChildStateApi<Block::Hash> for ChildState<Block, Client>
Block: BlockT + 'static,
Client: Send + Sync + 'static,
{
type Metadata = crate::metadata::Metadata;
type Metadata = crate::Metadata;
fn storage(
&self,
+4 -4
View File
@@ -373,7 +373,7 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli
fn subscribe_runtime_version(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<RuntimeVersion>,
) {
let stream = match self.client.storage_changes_notification_stream(
@@ -424,7 +424,7 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli
fn unsubscribe_runtime_version(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions.cancel(id))
@@ -432,7 +432,7 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli
fn subscribe_storage(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
keys: Option<Vec<StorageKey>>,
) {
@@ -484,7 +484,7 @@ impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Cli
fn unsubscribe_storage(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions.cancel(id))
@@ -289,7 +289,7 @@ impl<Block, F, Client> StateBackend<Block, Client> for LightState<Block, F, Clie
fn subscribe_storage(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
keys: Option<Vec<StorageKey>>
) {
@@ -384,7 +384,7 @@ impl<Block, F, Client> StateBackend<Block, Client> for LightState<Block, F, Clie
fn unsubscribe_storage(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
if !self.subscriptions.cancel(id.clone()) {
@@ -412,7 +412,7 @@ impl<Block, F, Client> StateBackend<Block, Client> for LightState<Block, F, Clie
fn subscribe_runtime_version(
&self,
_meta: crate::metadata::Metadata,
_meta: crate::Metadata,
subscriber: Subscriber<RuntimeVersion>,
) {
self.subscriptions.add(subscriber, move |sink| {
@@ -459,7 +459,7 @@ impl<Block, F, Client> StateBackend<Block, Client> for LightState<Block, F, Clie
fn unsubscribe_runtime_version(
&self,
_meta: Option<crate::metadata::Metadata>,
_meta: Option<crate::Metadata>,
id: SubscriptionId,
) -> RpcResult<bool> {
Ok(self.subscriptions.cancel(id))
+1
View File
@@ -32,6 +32,7 @@ lazy_static::lazy_static! {
type Boxed01Future01 = Box<dyn future01::Future<Item = (), Error = ()> + Send + 'static>;
/// Executor for use in testing
pub struct TaskExecutor;
impl future01::Executor<Boxed01Future01> for TaskExecutor {
fn execute(
+7 -7
View File
@@ -73,17 +73,17 @@ pub trait RpcExtensionBuilder {
/// Returns an instance of the RPC extension for a particular `DenyUnsafe`
/// value, e.g. the RPC extension might not expose some unsafe methods.
fn build(&self, deny: sc_rpc::DenyUnsafe) -> Self::Output;
fn build(&self, deny: sc_rpc::DenyUnsafe, subscriptions: SubscriptionManager) -> Self::Output;
}
impl<F, R> RpcExtensionBuilder for F where
F: Fn(sc_rpc::DenyUnsafe) -> R,
F: Fn(sc_rpc::DenyUnsafe, SubscriptionManager) -> R,
R: sc_rpc::RpcExtension<sc_rpc::Metadata>,
{
type Output = R;
fn build(&self, deny: sc_rpc::DenyUnsafe) -> Self::Output {
(*self)(deny)
fn build(&self, deny: sc_rpc::DenyUnsafe, subscriptions: SubscriptionManager) -> Self::Output {
(*self)(deny, subscriptions)
}
}
@@ -97,7 +97,7 @@ impl<R> RpcExtensionBuilder for NoopRpcExtensionBuilder<R> where
{
type Output = R;
fn build(&self, _deny: sc_rpc::DenyUnsafe) -> Self::Output {
fn build(&self, _deny: sc_rpc::DenyUnsafe, _subscriptions: SubscriptionManager) -> Self::Output {
self.0.clone()
}
}
@@ -764,7 +764,7 @@ fn gen_handler<TBl, TBackend, TExPool, TRpc, TCl>(
let author = sc_rpc::author::Author::new(
client,
transaction_pool,
subscriptions,
subscriptions.clone(),
keystore,
deny_unsafe,
);
@@ -786,7 +786,7 @@ fn gen_handler<TBl, TBackend, TExPool, TRpc, TCl>(
maybe_offchain_rpc,
author::AuthorApi::to_delegate(author),
system::SystemApi::to_delegate(system),
rpc_extensions_builder.build(deny_unsafe),
rpc_extensions_builder.build(deny_unsafe, subscriptions),
))
}