Skip to content

Commit

Permalink
grandpa: rewrite warp sync proof generation (paritytech#8148)
Browse files Browse the repository at this point in the history
* grandpa: use AuthoritySetChanges to generate warp sync proof

* node: init grandpa warp sync protocol

* grandpa: iterator for AuthoritySetChanges

* grandpa: rewrite warp sync proof generation

* grandpa: remove old code for warp sync generation

* grandpa: fix indentation

* grandpa: fix off by one

* grandpa: use binary search to find start idx when generating warp sync proof

* grandpa: add method to verify warp sync proofs

* grandpa: remove unnecessary code to skip authority set changes

* grandpa: add test for warp sync proof generation and verification

* grandpa: add missing docs

* grandpa: remove trailing comma
  • Loading branch information
andresilva authored and jam10o-new committed Feb 28, 2021
1 parent 5ed8ad7 commit ac42268
Show file tree
Hide file tree
Showing 10 changed files with 463 additions and 538 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions bin/node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,14 @@ 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(),
));
config.network.request_response_protocols.push(
sc_finality_grandpa_warp_sync::request_response_config_for_chain(
&config,
task_manager.spawn_handle(),
backend.clone(),
import_setup.1.shared_authority_set().clone(),
)
);

let (network, network_status_sinks, system_rpc_tx, network_starter) =
sc_service::build_network(sc_service::BuildNetworkParams {
Expand Down
27 changes: 18 additions & 9 deletions client/finality-grandpa-warp-sync/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,25 @@ repository = "https://github.com/paritytech/substrate/"
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
sc-network = { version = "0.9.0", path = "../network" }
sc-finality-grandpa = { version = "0.9.0", path = "../finality-grandpa" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sc-client-api = { version = "3.0.0", path = "../api" }
sc-service = { version = "0.9.0", path = "../service" }
codec = { package = "parity-scale-codec", version = "2.0.0" }
derive_more = "0.99.11"
futures = "0.3.8"
log = "0.4.11"
derive_more = "0.99.11"
codec = { package = "parity-scale-codec", version = "2.0.0" }
prost = "0.7"
num-traits = "0.2.14"
parking_lot = "0.11.1"
prost = "0.7"
sc-client-api = { version = "3.0.0", path = "../api" }
sc-finality-grandpa = { version = "0.9.0", path = "../finality-grandpa" }
sc-network = { version = "0.9.0", path = "../network" }
sc-service = { version = "0.9.0", path = "../service" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sp-finality-grandpa = { version = "3.0.0", path = "../../primitives/finality-grandpa" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }

[dev-dependencies]
finality-grandpa = { version = "0.14.0" }
rand = "0.8"
sc-block-builder = { version = "0.9.0", path = "../block-builder" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
sp-keyring = { version = "3.0.0", path = "../../primitives/keyring" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
61 changes: 37 additions & 24 deletions client/finality-grandpa-warp-sync/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer.

use codec::Decode;
use codec::{Decode, Encode};
use sc_network::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig};
use sc_client_api::Backend;
use sp_runtime::traits::NumberFor;
Expand All @@ -27,13 +27,18 @@ 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;
use sc_finality_grandpa::SharedAuthoritySet;

mod proof;

pub use proof::{AuthoritySetChangeProof, WarpSyncProof};

/// 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>,
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
) -> RequestResponseConfig
where NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
{
Expand All @@ -47,6 +52,7 @@ pub fn request_response_config_for_chain<TBlock: BlockT, TBackend: Backend<TBloc
let (handler, request_response_config) = GrandpaWarpSyncRequestHandler::new(
protocol_id.clone(),
backend.clone(),
authority_set,
);
spawn_handle.spawn("grandpa_warp_sync_request_handler", handler.run());
request_response_config
Expand Down Expand Up @@ -75,38 +81,40 @@ fn generate_protocol_name(protocol_id: ProtocolId) -> String {
s
}

#[derive(codec::Decode)]
#[derive(Decode)]
struct Request<B: BlockT> {
begin: B::Hash
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, but setting it to the max number of fragments
/// we return seems like a good idea until then.
const WARP_SYNC_CACHE_SIZE: usize = WARP_SYNC_FRAGMENTS_LIMIT;

/// 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>>>,
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
request_receiver: mpsc::Receiver<IncomingRequest>,
_phantom: std::marker::PhantomData<TBlock>
_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) {
pub fn new(
protocol_id: ProtocolId,
backend: Arc<TBackend>,
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
) -> (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)
(
Self {
backend,
request_receiver,
_phantom: std::marker::PhantomData,
authority_set,
},
request_response_config,
)
}

fn handle_request(
Expand All @@ -118,13 +126,14 @@ impl<TBlock: BlockT, TBackend: Backend<TBlock>> GrandpaWarpSyncRequestHandler<TB
{
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)
let proof = WarpSyncProof::generate(
self.backend.blockchain(),
request.begin,
&self.authority_set.authority_set_changes(),
)?;

pending_response.send(OutgoingResponse {
result: Ok(response),
result: Ok(proof.encode()),
reputation_changes: Vec::new(),
}).map_err(|_| HandleRequestError::SendResponse)
}
Expand All @@ -148,15 +157,19 @@ impl<TBlock: BlockT, TBackend: Backend<TBlock>> GrandpaWarpSyncRequestHandler<TB
}
}

#[derive(derive_more::Display, derive_more::From)]
enum HandleRequestError {
#[derive(Debug, derive_more::Display, derive_more::From)]
pub 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),
#[from(ignore)]
InvalidRequest(String),
#[from(ignore)]
InvalidProof(String),
#[display(fmt = "Failed to send response.")]
SendResponse,
}
Loading

0 comments on commit ac42268

Please sign in to comment.