mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 08:11:04 +00:00
Grandpa warp sync request-response protocol (#7711)
* Made a start * So the proof between authority set is phragmen one, this is crazy big, or is there some signing of the result : that is the storage key, damn? * ok getting from header digest seems doable. * for testing * get set id from storage directly (should use runtime to handler change). * move test to init * correct auth key * fix iteration * Correct proof content * actually update block number. * actually check last justif against its header * justification relation to new authorities through header hash check is needed here. This assumes the hash from header is calculated. * Few changes * Connected up cheme's branch * Clean up * Move things around a bit so that adding the grandpa warp sync request response protocol happens in the node code * Nits * Changes to comments * Cheme changes * Remove todos and test compile. * Rename _authority_ related proof function to _warp_sync_ . * Update client/grandpa-warp-sync/src/lib.rs quick fix * Put the warp sync request response protocol behind a feature flag because we dont' need it on a light client. * Update client/grandpa-warp-sync/src/lib.rs Quick fix * Update Cargo.lock * Adding test, comment on limitation related to 'delay', this could be implemented but with a cost. * Set between a delay override last fragment. * Check for pending authority set change at start. * adjust index * custom cache is not a good idea. * Use a simple cache instead. * restore broken indentation * Address crate rename * Merge conflict badly resolved, sorry Co-authored-by: cheme <emericchevalier.pro@gmail.com> Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
This commit is contained in:
Generated
+70
-17
@@ -937,7 +937,7 @@ dependencies = [
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"itertools 0.9.0",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
@@ -959,7 +959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2530,6 +2530,15 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
@@ -2878,7 +2887,7 @@ dependencies = [
|
||||
"parity-multiaddr",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-project 1.0.2",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
"ring",
|
||||
@@ -2935,7 +2944,7 @@ dependencies = [
|
||||
"libp2p-core",
|
||||
"libp2p-swarm",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
"smallvec 1.5.0",
|
||||
@@ -2957,7 +2966,7 @@ dependencies = [
|
||||
"libp2p-core",
|
||||
"libp2p-swarm",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
"regex",
|
||||
@@ -2977,7 +2986,7 @@ dependencies = [
|
||||
"libp2p-core",
|
||||
"libp2p-swarm",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"smallvec 1.5.0",
|
||||
"wasm-timer",
|
||||
@@ -2998,7 +3007,7 @@ dependencies = [
|
||||
"libp2p-core",
|
||||
"libp2p-swarm",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
"sha2 0.9.2",
|
||||
@@ -3060,7 +3069,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
"sha2 0.9.2",
|
||||
@@ -3096,7 +3105,7 @@ dependencies = [
|
||||
"futures 0.3.9",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"unsigned-varint 0.6.0",
|
||||
"void",
|
||||
@@ -3773,6 +3782,7 @@ dependencies = [
|
||||
"sc-consensus-epochs",
|
||||
"sc-consensus-slots",
|
||||
"sc-finality-grandpa",
|
||||
"sc-finality-grandpa-warp-sync",
|
||||
"sc-keystore",
|
||||
"sc-network",
|
||||
"sc-offchain",
|
||||
@@ -5765,6 +5775,16 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"prost-derive 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.7.0"
|
||||
@@ -5772,7 +5792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2"
|
||||
dependencies = [
|
||||
"bytes 1.0.1",
|
||||
"prost-derive",
|
||||
"prost-derive 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5783,16 +5803,29 @@ checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3"
|
||||
dependencies = [
|
||||
"bytes 1.0.1",
|
||||
"heck",
|
||||
"itertools",
|
||||
"itertools 0.9.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-types",
|
||||
"tempfile",
|
||||
"which 4.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.8.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.7.0"
|
||||
@@ -5800,7 +5833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"itertools 0.9.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -5813,7 +5846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
|
||||
dependencies = [
|
||||
"bytes 1.0.1",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6435,7 +6468,7 @@ dependencies = [
|
||||
"libp2p",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"quickcheck",
|
||||
"rand 0.7.3",
|
||||
@@ -6977,6 +7010,7 @@ dependencies = [
|
||||
"fork-tree",
|
||||
"futures 0.3.9",
|
||||
"futures-timer 3.0.2",
|
||||
"linked-hash-map",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.11.1",
|
||||
@@ -7042,6 +7076,25 @@ dependencies = [
|
||||
"substrate-test-runtime-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sc-finality-grandpa-warp-sync"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"futures 0.3.9",
|
||||
"log",
|
||||
"num-traits",
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.11.1",
|
||||
"prost 0.6.1",
|
||||
"sc-client-api",
|
||||
"sc-finality-grandpa",
|
||||
"sc-network",
|
||||
"sc-service",
|
||||
"sp-blockchain",
|
||||
"sp-runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sc-informant"
|
||||
version = "0.8.1"
|
||||
@@ -7126,7 +7179,7 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-project 0.4.27",
|
||||
"prost",
|
||||
"prost 0.7.0",
|
||||
"prost-build",
|
||||
"quickcheck",
|
||||
"rand 0.7.3",
|
||||
@@ -10480,6 +10533,6 @@ checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"glob",
|
||||
"itertools",
|
||||
"itertools 0.9.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -37,6 +37,7 @@ members = [
|
||||
"client/executor/wasmi",
|
||||
"client/executor/wasmtime",
|
||||
"client/finality-grandpa",
|
||||
"client/finality-grandpa-warp-sync",
|
||||
"client/informant",
|
||||
"client/keystore",
|
||||
"client/light",
|
||||
|
||||
@@ -74,6 +74,7 @@ sc-service = { version = "0.8.0", default-features = false, path = "../../../cli
|
||||
sc-tracing = { version = "2.0.0", path = "../../../client/tracing" }
|
||||
sc-telemetry = { version = "2.0.0", path = "../../../client/telemetry" }
|
||||
sc-authority-discovery = { version = "0.8.0", path = "../../../client/authority-discovery" }
|
||||
sc-finality-grandpa-warp-sync = { version = "0.8.0", path = "../../../client/finality-grandpa-warp-sync", optional = true }
|
||||
|
||||
# frame dependencies
|
||||
pallet-indices = { version = "2.0.0", path = "../../../frame/indices" }
|
||||
@@ -151,6 +152,7 @@ cli = [
|
||||
"frame-benchmarking-cli",
|
||||
"substrate-frame-cli",
|
||||
"sc-service/db",
|
||||
"sc-finality-grandpa-warp-sync",
|
||||
"structopt",
|
||||
"substrate-build-script-utils",
|
||||
]
|
||||
|
||||
@@ -194,6 +194,11 @@ pub fn new_full_base(
|
||||
|
||||
config.network.extra_sets.push(grandpa::grandpa_peers_set_config());
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
config.network.request_response_protocols.push(sc_finality_grandpa_warp_sync::request_response_config_for_chain(
|
||||
&config, task_manager.spawn_handle(), backend.clone(),
|
||||
));
|
||||
|
||||
let (network, network_status_sinks, system_rpc_tx, network_starter) =
|
||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||
config: &config,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
description = "A request-response protocol for handling grandpa warp sync requests"
|
||||
name = "sc-finality-grandpa-warp-sync"
|
||||
version = "0.8.0"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
sc-network = { version = "0.8.0", path = "../network" }
|
||||
sc-finality-grandpa = { version = "0.8.0", path = "../finality-grandpa" }
|
||||
sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" }
|
||||
sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" }
|
||||
sc-client-api = { version = "2.0.0", path = "../api" }
|
||||
sc-service = { version = "0.8.0", path = "../service" }
|
||||
futures = "0.3.8"
|
||||
log = "0.4.11"
|
||||
derive_more = "0.99.11"
|
||||
codec = { package = "parity-scale-codec", version = "1.3.5" }
|
||||
prost = "0.6.1"
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright 2021 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/>.
|
||||
|
||||
//! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer via the
|
||||
//! [`crate::request_responses::RequestResponsesBehaviour`].
|
||||
|
||||
use codec::Decode;
|
||||
use sc_network::config::{ProtocolId, IncomingRequest, RequestResponseConfig};
|
||||
use sc_client_api::Backend;
|
||||
use sp_runtime::traits::NumberFor;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use futures::stream::StreamExt;
|
||||
use log::debug;
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::time::Duration;
|
||||
use std::sync::Arc;
|
||||
use sc_service::{SpawnTaskHandle, config::{Configuration, Role}};
|
||||
use sc_finality_grandpa::WarpSyncFragmentCache;
|
||||
|
||||
/// Generates the appropriate [`RequestResponseConfig`] for a given chain configuration.
|
||||
pub fn request_response_config_for_chain<TBlock: BlockT, TBackend: Backend<TBlock> + 'static>(
|
||||
config: &Configuration,
|
||||
spawn_handle: SpawnTaskHandle,
|
||||
backend: Arc<TBackend>,
|
||||
) -> RequestResponseConfig
|
||||
where NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
let protocol_id = config.protocol_id();
|
||||
|
||||
if matches!(config.role, Role::Light) {
|
||||
// Allow outgoing requests but deny incoming requests.
|
||||
generate_request_response_config(protocol_id.clone())
|
||||
} else {
|
||||
// Allow both outgoing and incoming requests.
|
||||
let (handler, request_response_config) = GrandpaWarpSyncRequestHandler::new(
|
||||
protocol_id.clone(),
|
||||
backend.clone(),
|
||||
);
|
||||
spawn_handle.spawn("grandpa_warp_sync_request_handler", handler.run());
|
||||
request_response_config
|
||||
}
|
||||
}
|
||||
|
||||
const LOG_TARGET: &str = "finality-grandpa-warp-sync-request-handler";
|
||||
|
||||
/// Generates a [`RequestResponseConfig`] for the grandpa warp sync request protocol, refusing incoming requests.
|
||||
pub fn generate_request_response_config(protocol_id: ProtocolId) -> RequestResponseConfig {
|
||||
RequestResponseConfig {
|
||||
name: generate_protocol_name(protocol_id).into(),
|
||||
max_request_size: 32,
|
||||
max_response_size: 16 * 1024 * 1024,
|
||||
request_timeout: Duration::from_secs(10),
|
||||
inbound_queue: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the grandpa warp sync protocol name from chain specific protocol identifier.
|
||||
fn generate_protocol_name(protocol_id: ProtocolId) -> String {
|
||||
let mut s = String::new();
|
||||
s.push_str("/");
|
||||
s.push_str(protocol_id.as_ref());
|
||||
s.push_str("/sync/warp");
|
||||
s
|
||||
}
|
||||
|
||||
#[derive(codec::Decode)]
|
||||
struct Request<B: BlockT> {
|
||||
begin: B::Hash
|
||||
}
|
||||
|
||||
/// Setting a large fragment limit, allowing client
|
||||
/// to define it is possible.
|
||||
const WARP_SYNC_FRAGMENTS_LIMIT: usize = 100;
|
||||
|
||||
/// Number of item with justification in warp sync cache.
|
||||
/// This should be customizable, setting a low number
|
||||
/// until then.
|
||||
const WARP_SYNC_CACHE_SIZE: usize = 20;
|
||||
|
||||
/// Handler for incoming grandpa warp sync requests from a remote peer.
|
||||
pub struct GrandpaWarpSyncRequestHandler<TBackend, TBlock: BlockT> {
|
||||
backend: Arc<TBackend>,
|
||||
cache: Arc<parking_lot::RwLock<WarpSyncFragmentCache<TBlock::Header>>>,
|
||||
request_receiver: mpsc::Receiver<IncomingRequest>,
|
||||
_phantom: std::marker::PhantomData<TBlock>
|
||||
}
|
||||
|
||||
impl<TBlock: BlockT, TBackend: Backend<TBlock>> GrandpaWarpSyncRequestHandler<TBackend, TBlock> {
|
||||
/// Create a new [`GrandpaWarpSyncRequestHandler`].
|
||||
pub fn new(protocol_id: ProtocolId, backend: Arc<TBackend>) -> (Self, RequestResponseConfig) {
|
||||
let (tx, request_receiver) = mpsc::channel(20);
|
||||
|
||||
let mut request_response_config = generate_request_response_config(protocol_id);
|
||||
request_response_config.inbound_queue = Some(tx);
|
||||
let cache = Arc::new(parking_lot::RwLock::new(WarpSyncFragmentCache::new(WARP_SYNC_CACHE_SIZE)));
|
||||
|
||||
(Self { backend, request_receiver, cache, _phantom: std::marker::PhantomData }, request_response_config)
|
||||
}
|
||||
|
||||
fn handle_request(
|
||||
&self,
|
||||
payload: Vec<u8>,
|
||||
pending_response: oneshot::Sender<Vec<u8>>
|
||||
) -> Result<(), HandleRequestError>
|
||||
where NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
let request = Request::<TBlock>::decode(&mut &payload[..])?;
|
||||
|
||||
let mut cache = self.cache.write();
|
||||
let response = sc_finality_grandpa::prove_warp_sync(
|
||||
self.backend.blockchain(), request.begin, Some(WARP_SYNC_FRAGMENTS_LIMIT), Some(&mut cache)
|
||||
)?;
|
||||
|
||||
pending_response.send(response)
|
||||
.map_err(|_| HandleRequestError::SendResponse)
|
||||
}
|
||||
|
||||
/// Run [`GrandpaWarpSyncRequestHandler`].
|
||||
pub async fn run(mut self)
|
||||
where NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
while let Some(request) = self.request_receiver.next().await {
|
||||
let IncomingRequest { peer, payload, pending_response } = request;
|
||||
|
||||
match self.handle_request(payload, pending_response) {
|
||||
Ok(()) => debug!(target: LOG_TARGET, "Handled grandpa warp sync request from {}.", peer),
|
||||
Err(e) => debug!(
|
||||
target: LOG_TARGET,
|
||||
"Failed to handle grandpa warp sync request from {}: {}",
|
||||
peer, e,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(derive_more::Display, derive_more::From)]
|
||||
enum HandleRequestError {
|
||||
#[display(fmt = "Failed to decode request: {}.", _0)]
|
||||
DecodeProto(prost::DecodeError),
|
||||
#[display(fmt = "Failed to encode response: {}.", _0)]
|
||||
EncodeProto(prost::EncodeError),
|
||||
#[display(fmt = "Failed to decode block hash: {}.", _0)]
|
||||
DecodeScale(codec::Error),
|
||||
Client(sp_blockchain::Error),
|
||||
#[display(fmt = "Failed to send response.")]
|
||||
SendResponse,
|
||||
}
|
||||
@@ -45,6 +45,7 @@ prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../..
|
||||
sc-block-builder = { version = "0.8.0", path = "../block-builder" }
|
||||
finality-grandpa = { version = "0.12.3", features = ["derive-codec"] }
|
||||
pin-project = "0.4.6"
|
||||
linked-hash-map = "0.5.2"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
|
||||
@@ -52,7 +52,7 @@ use parity_scale_codec::{Encode, Decode};
|
||||
use finality_grandpa::BlockNumberOps;
|
||||
use sp_runtime::{
|
||||
Justification, generic::BlockId,
|
||||
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
|
||||
traits::{NumberFor, Block as BlockT, Header as HeaderT, Zero, One},
|
||||
};
|
||||
use sp_core::storage::StorageKey;
|
||||
use sc_telemetry::{telemetry, CONSENSUS_INFO};
|
||||
@@ -247,6 +247,23 @@ pub struct FinalityProofFragment<Header: HeaderT> {
|
||||
/// - all other fragments provide justifications for GRANDPA authorities set changes within requested range.
|
||||
type FinalityProof<Header> = Vec<FinalityProofFragment<Header>>;
|
||||
|
||||
/// Single fragment of authority set proof.
|
||||
///
|
||||
/// Finality for block B is proved by providing:
|
||||
/// 1) headers of this block;
|
||||
/// 2) the justification for the block containing a authority set change digest;
|
||||
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
|
||||
pub(crate) struct AuthoritySetProofFragment<Header: HeaderT> {
|
||||
/// The header of the given block.
|
||||
pub header: Header,
|
||||
/// Justification of the block F.
|
||||
pub justification: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Proof of authority set is the ordered set of authority set fragments, where:
|
||||
/// - last fragment match target block.
|
||||
type AuthoritySetProof<Header> = Vec<AuthoritySetProofFragment<Header>>;
|
||||
|
||||
/// Finality proof request data.
|
||||
#[derive(Debug, Encode, Decode)]
|
||||
enum FinalityProofRequest<H: Encode + Decode> {
|
||||
@@ -425,6 +442,133 @@ pub(crate) fn prove_finality<Block: BlockT, B: BlockchainBackend<Block>, J>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare authority proof for the best possible block starting at a given trusted block.
|
||||
///
|
||||
/// Started block should be in range of bonding duration.
|
||||
/// We only return proof for finalized blocks (with justification).
|
||||
///
|
||||
/// It is assumed that the caller already have a proof-of-finality for the block 'begin'.
|
||||
pub fn prove_warp_sync<Block: BlockT, B: BlockchainBackend<Block>>(
|
||||
blockchain: &B,
|
||||
begin: Block::Hash,
|
||||
max_fragment_limit: Option<usize>,
|
||||
mut cache: Option<&mut WarpSyncFragmentCache<Block::Header>>,
|
||||
) -> ::sp_blockchain::Result<Vec<u8>> {
|
||||
|
||||
let begin = BlockId::Hash(begin);
|
||||
let begin_number = blockchain.block_number_from_id(&begin)?
|
||||
.ok_or_else(|| ClientError::Backend("Missing start block".to_string()))?;
|
||||
let end = BlockId::Hash(blockchain.last_finalized()?);
|
||||
let end_number = blockchain.block_number_from_id(&end)?
|
||||
// This error should not happen, we could also panic.
|
||||
.ok_or_else(|| ClientError::Backend("Missing last finalized block".to_string()))?;
|
||||
|
||||
if begin_number > end_number {
|
||||
return Err(ClientError::Backend("Unfinalized start for authority proof".to_string()));
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut last_apply = None;
|
||||
|
||||
let header = blockchain.expect_header(begin)?;
|
||||
let mut index = *header.number();
|
||||
|
||||
// Find previous change in case there is a delay.
|
||||
// This operation is a costy and only for the delay corner case.
|
||||
while index > Zero::zero() {
|
||||
index = index - One::one();
|
||||
if let Some((fragement, apply_block)) = get_warp_sync_proof_fragment(blockchain, index, &mut cache)? {
|
||||
if last_apply.map(|next| &next > header.number()).unwrap_or(false) {
|
||||
result.push(fragement);
|
||||
last_apply = Some(apply_block);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut index = *header.number();
|
||||
while index <= end_number {
|
||||
if max_fragment_limit.map(|limit| result.len() <= limit).unwrap_or(false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some((fragement, apply_block)) = get_warp_sync_proof_fragment(blockchain, index, &mut cache)? {
|
||||
if last_apply.map(|next| apply_block < next).unwrap_or(false) {
|
||||
// Previous delayed will not apply, do not include it.
|
||||
result.pop();
|
||||
}
|
||||
result.push(fragement);
|
||||
last_apply = Some(apply_block);
|
||||
}
|
||||
|
||||
index = index + One::one();
|
||||
}
|
||||
|
||||
if result.last().as_ref().map(|head| head.header.number()) != Some(&end_number) {
|
||||
let header = blockchain.expect_header(end)?;
|
||||
if let Some(justification) = blockchain.justification(BlockId::Number(end_number.clone()))? {
|
||||
result.push(AuthoritySetProofFragment {
|
||||
header: header.clone(),
|
||||
justification,
|
||||
});
|
||||
} else {
|
||||
// no justification, don't include it.
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result.encode())
|
||||
}
|
||||
|
||||
/// Try get a warp sync proof fragment a a given finalized block.
|
||||
fn get_warp_sync_proof_fragment<Block: BlockT, B: BlockchainBackend<Block>>(
|
||||
blockchain: &B,
|
||||
index: NumberFor<Block>,
|
||||
cache: &mut Option<&mut WarpSyncFragmentCache<Block::Header>>,
|
||||
) -> sp_blockchain::Result<Option<(AuthoritySetProofFragment<Block::Header>, NumberFor<Block>)>> {
|
||||
if let Some(cache) = cache.as_mut() {
|
||||
if let Some(result) = cache.get_item(index) {
|
||||
return Ok(result.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = None;
|
||||
let header = blockchain.expect_header(BlockId::number(index))?;
|
||||
|
||||
if let Some((block_number, sp_finality_grandpa::ScheduledChange {
|
||||
next_authorities: _,
|
||||
delay,
|
||||
})) = crate::import::find_forced_change::<Block>(&header) {
|
||||
let dest = block_number + delay;
|
||||
if let Some(justification) = blockchain.justification(BlockId::Number(index.clone()))? {
|
||||
result = Some((AuthoritySetProofFragment {
|
||||
header: header.clone(),
|
||||
justification,
|
||||
}, dest));
|
||||
} else {
|
||||
return Err(ClientError::Backend("Unjustified block with authority set change".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(sp_finality_grandpa::ScheduledChange {
|
||||
next_authorities: _,
|
||||
delay,
|
||||
}) = crate::import::find_scheduled_change::<Block>(&header) {
|
||||
let dest = index + delay;
|
||||
if let Some(justification) = blockchain.justification(BlockId::Number(index.clone()))? {
|
||||
result = Some((AuthoritySetProofFragment {
|
||||
header: header.clone(),
|
||||
justification,
|
||||
}, dest));
|
||||
} else {
|
||||
return Err(ClientError::Backend("Unjustified block with authority set change".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
cache.as_mut().map(|cache| cache.new_item(index, result.clone()));
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Check GRANDPA proof-of-finality for the given block.
|
||||
///
|
||||
/// Returns the vector of headers that MUST be validated + imported
|
||||
@@ -483,6 +627,98 @@ pub(crate) fn check_finality_proof<Block: BlockT, B, J>(
|
||||
Ok(effects)
|
||||
}
|
||||
|
||||
/// Check GRANDPA authority change sequence to assert finality of a target block.
|
||||
///
|
||||
/// Returns the header of the target block.
|
||||
pub(crate) fn check_warp_sync_proof<Block: BlockT, J>(
|
||||
current_set_id: u64,
|
||||
current_authorities: AuthorityList,
|
||||
remote_proof: Vec<u8>,
|
||||
) -> ClientResult<(Block::Header, u64, AuthorityList)>
|
||||
where
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
J: Decode + ProvableJustification<Block::Header> + BlockJustification<Block::Header>,
|
||||
{
|
||||
// decode finality proof
|
||||
let proof = AuthoritySetProof::<Block::Header>::decode(&mut &remote_proof[..])
|
||||
.map_err(|_| ClientError::BadJustification("failed to decode authority proof".into()))?;
|
||||
|
||||
let last = proof.len() - 1;
|
||||
|
||||
let mut result = (current_set_id, current_authorities, NumberFor::<Block>::zero());
|
||||
|
||||
for (ix, fragment) in proof.into_iter().enumerate() {
|
||||
let is_last = ix == last;
|
||||
result = check_warp_sync_proof_fragment::<Block, J>(
|
||||
result.0,
|
||||
&result.1,
|
||||
&result.2,
|
||||
is_last,
|
||||
&fragment,
|
||||
)?;
|
||||
|
||||
if is_last {
|
||||
return Ok((fragment.header, result.0, result.1))
|
||||
}
|
||||
}
|
||||
|
||||
// empty proof can't prove anything
|
||||
return Err(ClientError::BadJustification("empty proof of authority".into()));
|
||||
}
|
||||
|
||||
/// Check finality authority set sequence.
|
||||
fn check_warp_sync_proof_fragment<Block: BlockT, J>(
|
||||
current_set_id: u64,
|
||||
current_authorities: &AuthorityList,
|
||||
previous_checked_block: &NumberFor<Block>,
|
||||
is_last: bool,
|
||||
authorities_proof: &AuthoritySetProofFragment<Block::Header>,
|
||||
) -> ClientResult<(u64, AuthorityList, NumberFor<Block>)>
|
||||
where
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
J: Decode + ProvableJustification<Block::Header> + BlockJustification<Block::Header>,
|
||||
{
|
||||
let justification: J = Decode::decode(&mut authorities_proof.justification.as_slice())
|
||||
.map_err(|_| ClientError::JustificationDecode)?;
|
||||
justification.verify(current_set_id, ¤t_authorities)?;
|
||||
|
||||
// assert justification is for this header
|
||||
if &justification.number() != authorities_proof.header.number()
|
||||
|| justification.hash().as_ref() != authorities_proof.header.hash().as_ref() {
|
||||
return Err(ClientError::Backend("Invalid authority warp proof, justification do not match header".to_string()));
|
||||
}
|
||||
|
||||
if authorities_proof.header.number() <= previous_checked_block {
|
||||
return Err(ClientError::Backend("Invalid authority warp proof".to_string()));
|
||||
}
|
||||
let current_block = authorities_proof.header.number();
|
||||
let mut at_block = None;
|
||||
if let Some(sp_finality_grandpa::ScheduledChange {
|
||||
next_authorities,
|
||||
delay,
|
||||
}) = crate::import::find_scheduled_change::<Block>(&authorities_proof.header) {
|
||||
let dest = *current_block + delay;
|
||||
at_block = Some((dest, next_authorities));
|
||||
}
|
||||
if let Some((block_number, sp_finality_grandpa::ScheduledChange {
|
||||
next_authorities,
|
||||
delay,
|
||||
})) = crate::import::find_forced_change::<Block>(&authorities_proof.header) {
|
||||
let dest = block_number + delay;
|
||||
at_block = Some((dest, next_authorities));
|
||||
}
|
||||
|
||||
// Fragment without change only allowed for proof last block.
|
||||
if at_block.is_none() && !is_last {
|
||||
return Err(ClientError::Backend("Invalid authority warp proof".to_string()));
|
||||
}
|
||||
if let Some((at_block, next_authorities)) = at_block {
|
||||
Ok((current_set_id + 1, next_authorities, at_block))
|
||||
} else {
|
||||
Ok((current_set_id, current_authorities.clone(), current_block.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Check finality proof for the single block.
|
||||
fn check_finality_proof_fragment<Block: BlockT, B, J>(
|
||||
blockchain: &B,
|
||||
@@ -551,6 +787,15 @@ impl<Header: HeaderT> AuthoritiesOrEffects<Header> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Block info extracted from the justification.
|
||||
pub(crate) trait BlockJustification<Header: HeaderT> {
|
||||
/// Block number justified.
|
||||
fn number(&self) -> Header::Number;
|
||||
|
||||
/// Block hash justified.
|
||||
fn hash(&self) -> Header::Hash;
|
||||
}
|
||||
|
||||
/// Justification used to prove block finality.
|
||||
pub(crate) trait ProvableJustification<Header: HeaderT>: Encode + Decode {
|
||||
/// Verify justification with respect to authorities set and authorities set id.
|
||||
@@ -582,6 +827,68 @@ impl<Block: BlockT> ProvableJustification<Block::Header> for GrandpaJustificatio
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockJustification<Block::Header> for GrandpaJustification<Block> {
|
||||
fn number(&self) -> NumberFor<Block> {
|
||||
self.commit.target_number.clone()
|
||||
}
|
||||
fn hash(&self) -> Block::Hash {
|
||||
self.commit.target_hash.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple cache for warp sync queries.
|
||||
pub struct WarpSyncFragmentCache<Header: HeaderT> {
|
||||
cache: linked_hash_map::LinkedHashMap<
|
||||
Header::Number,
|
||||
Option<(AuthoritySetProofFragment<Header>, Header::Number)>,
|
||||
>,
|
||||
headers_with_justification: usize,
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
impl<Header: HeaderT> WarpSyncFragmentCache<Header> {
|
||||
/// Instantiate a new cache for the warp sync prover.
|
||||
pub fn new(size: usize) -> Self {
|
||||
WarpSyncFragmentCache {
|
||||
cache: Default::default(),
|
||||
headers_with_justification: 0,
|
||||
limit: size,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_item(
|
||||
&mut self,
|
||||
at: Header::Number,
|
||||
item: Option<(AuthoritySetProofFragment<Header>, Header::Number)>,
|
||||
) {
|
||||
if self.cache.len() == self.limit {
|
||||
self.pop_one();
|
||||
}
|
||||
if item.is_some() {
|
||||
// we do not check previous value as cached value is always supposed to
|
||||
// be queried before calling 'new_item'.
|
||||
self.headers_with_justification += 1;
|
||||
}
|
||||
self.cache.insert(at, item);
|
||||
}
|
||||
|
||||
fn pop_one(&mut self) {
|
||||
while let Some(v) = self.cache.pop_front() {
|
||||
if v.1.is_some() {
|
||||
self.headers_with_justification -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_item(
|
||||
&mut self,
|
||||
block: Header::Number,
|
||||
) -> Option<&mut Option<(AuthoritySetProofFragment<Header>, Header::Number)>> {
|
||||
self.cache.get_refresh(&block)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use substrate_test_runtime_client::runtime::{Block, Header, H256};
|
||||
@@ -635,6 +942,24 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Encode, Decode)]
|
||||
pub struct TestBlockJustification(TestJustification, u64, H256);
|
||||
|
||||
impl BlockJustification<Header> for TestBlockJustification {
|
||||
fn number(&self) -> <Header as HeaderT>::Number {
|
||||
self.1
|
||||
}
|
||||
fn hash(&self) -> <Header as HeaderT>::Hash {
|
||||
self.2.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvableJustification<Header> for TestBlockJustification {
|
||||
fn verify(&self, set_id: u64, authorities: &[(AuthorityId, u64)]) -> ClientResult<()> {
|
||||
self.0.verify(set_id, authorities)
|
||||
}
|
||||
}
|
||||
|
||||
fn header(number: u64) -> Header {
|
||||
let parent_hash = match number {
|
||||
0 => Default::default(),
|
||||
@@ -1027,4 +1352,161 @@ pub(crate) mod tests {
|
||||
).unwrap();
|
||||
assert!(proof_of_4.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warp_sync_proof_encoding_decoding() {
|
||||
fn test_blockchain(
|
||||
nb_blocks: u64,
|
||||
mut set_change: &[(u64, Vec<u8>)],
|
||||
mut justifications: &[(u64, Vec<u8>)],
|
||||
) -> (InMemoryBlockchain<Block>, Vec<H256>) {
|
||||
let blockchain = InMemoryBlockchain::<Block>::new();
|
||||
let mut hashes = Vec::<H256>::new();
|
||||
let mut set_id = 0;
|
||||
for i in 0..nb_blocks {
|
||||
let mut set_id_next = set_id;
|
||||
let mut header = header(i);
|
||||
set_change.first()
|
||||
.map(|j| if i == j.0 {
|
||||
set_change = &set_change[1..];
|
||||
let next_authorities: Vec<_> = j.1.iter().map(|i| (AuthorityId::from_slice(&[*i; 32]), 1u64)).collect();
|
||||
set_id_next += 1;
|
||||
header.digest_mut().logs.push(
|
||||
sp_runtime::generic::DigestItem::Consensus(
|
||||
sp_finality_grandpa::GRANDPA_ENGINE_ID,
|
||||
sp_finality_grandpa::ConsensusLog::ScheduledChange(
|
||||
sp_finality_grandpa::ScheduledChange { delay: 0u64, next_authorities }
|
||||
).encode(),
|
||||
));
|
||||
});
|
||||
|
||||
if let Some(parent) = hashes.last() {
|
||||
header.set_parent_hash(parent.clone());
|
||||
}
|
||||
let header_hash = header.hash();
|
||||
|
||||
let justification = justifications.first()
|
||||
.and_then(|j| if i == j.0 {
|
||||
justifications = &justifications[1..];
|
||||
|
||||
let authority = j.1.iter().map(|j|
|
||||
(AuthorityId::from_slice(&[*j; 32]), 1u64)
|
||||
).collect();
|
||||
let justification = TestBlockJustification(
|
||||
TestJustification((set_id, authority), vec![i as u8]),
|
||||
i,
|
||||
header_hash,
|
||||
);
|
||||
Some(justification.encode())
|
||||
} else {
|
||||
None
|
||||
});
|
||||
hashes.push(header_hash.clone());
|
||||
set_id = set_id_next;
|
||||
|
||||
blockchain.insert(header_hash, header, justification, None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
}
|
||||
(blockchain, hashes)
|
||||
}
|
||||
|
||||
let (blockchain, hashes) = test_blockchain(
|
||||
7,
|
||||
vec![(3, vec![9])].as_slice(),
|
||||
vec![
|
||||
(1, vec![1, 2, 3]),
|
||||
(2, vec![1, 2, 3]),
|
||||
(3, vec![1, 2, 3]),
|
||||
(4, vec![9]),
|
||||
(6, vec![9]),
|
||||
].as_slice(),
|
||||
);
|
||||
|
||||
// proof after set change
|
||||
let mut cache = WarpSyncFragmentCache::new(5);
|
||||
let proof_no_cache = prove_warp_sync(&blockchain, hashes[6], None, Some(&mut cache)).unwrap();
|
||||
let proof = prove_warp_sync(&blockchain, hashes[6], None, Some(&mut cache)).unwrap();
|
||||
assert_eq!(proof_no_cache, proof);
|
||||
|
||||
let initial_authorities: Vec<_> = [1u8, 2, 3].iter().map(|i|
|
||||
(AuthorityId::from_slice(&[*i; 32]), 1u64)
|
||||
).collect();
|
||||
|
||||
let authorities_next: Vec<_> = [9u8].iter().map(|i|
|
||||
(AuthorityId::from_slice(&[*i; 32]), 1u64)
|
||||
).collect();
|
||||
|
||||
assert!(check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
0,
|
||||
initial_authorities.clone(),
|
||||
proof.clone(),
|
||||
).is_err());
|
||||
assert!(check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
0,
|
||||
authorities_next.clone(),
|
||||
proof.clone(),
|
||||
).is_err());
|
||||
assert!(check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
1,
|
||||
initial_authorities.clone(),
|
||||
proof.clone(),
|
||||
).is_err());
|
||||
let (
|
||||
_header,
|
||||
current_set_id,
|
||||
current_set,
|
||||
) = check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
1,
|
||||
authorities_next.clone(),
|
||||
proof.clone(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(current_set_id, 1);
|
||||
assert_eq!(current_set, authorities_next);
|
||||
|
||||
// proof before set change
|
||||
let proof = prove_warp_sync(&blockchain, hashes[1], None, None).unwrap();
|
||||
let (
|
||||
_header,
|
||||
current_set_id,
|
||||
current_set,
|
||||
) = check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
0,
|
||||
initial_authorities.clone(),
|
||||
proof.clone(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(current_set_id, 1);
|
||||
assert_eq!(current_set, authorities_next);
|
||||
|
||||
// two changes
|
||||
let (blockchain, hashes) = test_blockchain(
|
||||
13,
|
||||
vec![(3, vec![7]), (8, vec![9])].as_slice(),
|
||||
vec![
|
||||
(1, vec![1, 2, 3]),
|
||||
(2, vec![1, 2, 3]),
|
||||
(3, vec![1, 2, 3]),
|
||||
(4, vec![7]),
|
||||
(6, vec![7]),
|
||||
(8, vec![7]), // warning, requires a justification on change set
|
||||
(10, vec![9]),
|
||||
].as_slice(),
|
||||
);
|
||||
|
||||
// proof before set change
|
||||
let proof = prove_warp_sync(&blockchain, hashes[1], None, None).unwrap();
|
||||
let (
|
||||
_header,
|
||||
current_set_id,
|
||||
current_set,
|
||||
) = check_warp_sync_proof::<Block, TestBlockJustification>(
|
||||
0,
|
||||
initial_authorities.clone(),
|
||||
proof.clone(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(current_set_id, 2);
|
||||
assert_eq!(current_set, authorities_next);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl<'a, Block: 'a + BlockT> Drop for PendingSetChanges<'a, Block> {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_scheduled_change<B: BlockT>(header: &B::Header)
|
||||
pub(crate) fn find_scheduled_change<B: BlockT>(header: &B::Header)
|
||||
-> Option<ScheduledChange<NumberFor<B>>>
|
||||
{
|
||||
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
|
||||
@@ -197,7 +197,7 @@ fn find_scheduled_change<B: BlockT>(header: &B::Header)
|
||||
header.digest().convert_first(|l| l.try_to(id).and_then(filter_log))
|
||||
}
|
||||
|
||||
fn find_forced_change<B: BlockT>(header: &B::Header)
|
||||
pub(crate) fn find_forced_change<B: BlockT>(header: &B::Header)
|
||||
-> Option<(NumberFor<B>, ScheduledChange<NumberFor<B>>)>
|
||||
{
|
||||
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
|
||||
|
||||
@@ -130,6 +130,7 @@ pub use voting_rule::{
|
||||
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
|
||||
};
|
||||
pub use finality_grandpa::voter::report;
|
||||
pub use finality_proof::{prove_warp_sync, WarpSyncFragmentCache};
|
||||
|
||||
use aux_schema::PersistentData;
|
||||
use environment::{Environment, VoterSetState};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm, RpcHandlers, NetworkStatusSinks,
|
||||
error::Error, MallocSizeOfWasm, RpcHandlers, NetworkStatusSinks,
|
||||
start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle,
|
||||
metrics::MetricsService,
|
||||
client::{light, Client, ClientConfig},
|
||||
@@ -877,18 +877,7 @@ pub fn build_network<TBl, TExPool, TImpQu, TCl>(
|
||||
client: client.clone(),
|
||||
});
|
||||
|
||||
let protocol_id = {
|
||||
let protocol_id_full = match config.chain_spec.protocol_id() {
|
||||
Some(pid) => pid,
|
||||
None => {
|
||||
warn!("Using default protocol ID {:?} because none is configured in the \
|
||||
chain specs", DEFAULT_PROTOCOL_ID
|
||||
);
|
||||
DEFAULT_PROTOCOL_ID
|
||||
}
|
||||
};
|
||||
sc_network::config::ProtocolId::from(protocol_id_full)
|
||||
};
|
||||
let protocol_id = config.protocol_id();
|
||||
|
||||
let block_announce_validator = if let Some(f) = block_announce_validator_builder {
|
||||
f(client.clone())
|
||||
|
||||
@@ -219,6 +219,20 @@ impl Configuration {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the network protocol id from the chain spec, or the default.
|
||||
pub fn protocol_id(&self) -> sc_network::config::ProtocolId {
|
||||
let protocol_id_full = match self.chain_spec.protocol_id() {
|
||||
Some(pid) => pid,
|
||||
None => {
|
||||
log::warn!("Using default protocol ID {:?} because none is configured in the \
|
||||
chain specs", crate::DEFAULT_PROTOCOL_ID
|
||||
);
|
||||
crate::DEFAULT_PROTOCOL_ID
|
||||
}
|
||||
};
|
||||
sc_network::config::ProtocolId::from(protocol_id_full)
|
||||
}
|
||||
}
|
||||
|
||||
/// Available RPC methods.
|
||||
|
||||
Reference in New Issue
Block a user