From f41b6ffd3165fd7c613b32216679d6809466a9a3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 17 Jun 2021 18:58:31 +0200 Subject: [PATCH 01/20] Started warp sync --- client/network/src/config.rs | 2 + client/network/src/protocol.rs | 5 +- client/network/src/protocol/sync.rs | 114 +++++++++++++++-- client/network/src/protocol/sync/warp.rs | 150 +++++++++++++++++++++++ 4 files changed, 262 insertions(+), 9 deletions(-) create mode 100644 client/network/src/protocol/sync/warp.rs diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 36ae1e831b8ce..f141b568355e4 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -392,6 +392,8 @@ pub enum SyncMode { /// Skip state proof download and verification. skip_proofs: bool }, + /// GRANDPA Warp sync - verify authority set transitions and the latest state. + Warp, } impl Default for SyncMode { diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index b9a189a0f384f..8c68376c04b69 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -229,6 +229,7 @@ impl ProtocolConfig { match self.sync_mode { config::SyncMode::Full => sync::SyncMode::Full, config::SyncMode::Fast { skip_proofs } => sync::SyncMode::LightState { skip_proofs }, + config::SyncMode::Warp => sync::SyncMode::Warp } } } @@ -669,6 +670,7 @@ impl Protocol { Ok(sync::OnBlockData::Request(peer, req)) => { self.prepare_block_request(peer, req) } + Ok(sync::OnBlockData::Ignore) => CustomMessageOutcome::None, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); @@ -986,7 +988,8 @@ impl Protocol { }, Ok(sync::OnBlockData::Request(peer, req)) => { self.prepare_block_request(peer, req) - } + }, + Ok(sync::OnBlockData::Ignore) => CustomMessageOutcome::None, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 44fbe64bfcff4..b85792f3223e1 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -32,6 +32,7 @@ use codec::Encode; use blocks::BlockCollection; use state::StateSync; +use warp::WarpSync; use sp_blockchain::{Error as ClientError, HeaderMetadata}; use sp_consensus::{BlockOrigin, BlockStatus, block_validation::{BlockAnnounceValidator, Validation}, @@ -62,6 +63,7 @@ use futures::{task::Poll, Future, stream::FuturesUnordered, FutureExt, StreamExt mod blocks; mod extra_requests; mod state; +mod warp; /// Maximum blocks to request in a single packet. const MAX_BLOCKS_TO_REQUEST: usize = 128; @@ -214,6 +216,8 @@ pub struct ChainSync { block_announce_validation_per_peer_stats: HashMap, /// State sync in progress, if any. state_sync: Option>, + /// Warp sync in progress, if any. + warp_sync: Option>, /// Enable importing existing blocks. This is used used after the state download to /// catch up to the latest state while re-importing blocks. import_existing: bool, @@ -330,6 +334,8 @@ pub struct Status { pub queued_blocks: u32, /// State sync status in progress, if any. pub state_sync: Option, + /// Warp sync in progress, if any. + pub warp_sync: Option, } /// A peer did not behave as expected and should be reported. @@ -350,7 +356,9 @@ pub enum OnBlockData { /// The block should be imported. Import(BlockOrigin, Vec>), /// A new block request needs to be made to the given peer. - Request(PeerId, BlockRequest) + Request(PeerId, BlockRequest), + /// Block is processed internally and no furter action is requried. + Ignore, } impl OnBlockData { @@ -471,6 +479,8 @@ pub enum SyncMode { LightState { skip_proofs: bool }, + // GRANDPA warp sync mode. + Warp, } /// Result of [`ChainSync::has_slot_for_block_announce_validation`]. @@ -508,6 +518,7 @@ impl ChainSync { block_announce_validation: Default::default(), block_announce_validation_per_peer_stats: Default::default(), state_sync: None, + warp_sync: None, import_existing: false, }; sync.reset_sync_start_point()?; @@ -518,7 +529,7 @@ impl ChainSync { match self.mode { SyncMode::Full => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, SyncMode::Light => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION, - SyncMode::LightState { .. } => + SyncMode::LightState { .. } | SyncMode::Warp => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, } } @@ -528,6 +539,7 @@ impl ChainSync { SyncMode::Full => false, SyncMode::Light => true, SyncMode::LightState { .. } => true, + SyncMode::Warp => true, } } @@ -560,6 +572,7 @@ impl ChainSync { num_peers: self.peers.len() as u32, queued_blocks: self.queue_blocks.len() as u32, state_sync: self.state_sync.as_ref().map(|s| s.progress()), + warp_sync: self.warp_sync.as_ref().map(|s| s.progress()), } } @@ -615,6 +628,20 @@ impl ChainSync { return Ok(None) } + if let SyncMode::Warp = &self.mode { + // TODO: wait for 3 peers + if self.warp_sync.is_none() { + let number = best_number; + let hash = best_hash; + log::debug!( + target: "sync", + "Starting warp state sync for #{} ({})", + number, + hash, + ); + self.warp_sync = Some(WarpSync::new(self.client.clone(), hash, number)); + } + } // If we are at genesis, just start downloading. let (state, req) = if self.best_queued_number.is_zero() { debug!( @@ -776,12 +803,40 @@ impl ChainSync { /// Get an iterator over all block requests of all peers. pub fn block_requests(&mut self) -> impl Iterator)> + '_ { + if let Some(warp_sync) = &self.warp_sync { + if let Some(request) = warp_sync.next_block_request() { + // Find a peer that can handle the request. + let peer = self.peers.iter().find_map(move |(id, peer)| { + if !peer.state.is_available() { + return None + } + + if peer.best_number >= warp_sync.target_block_number() { + Some((id, peer.best_number)) + } else { + None + } + }); + if let Some((peer, best_num)) = peer { + trace!( + target: "sync", + "New warp block request for {}, (best:{}, target:{}) {:?}", + peer, + best_num, + warp_sync.target_block_number(), + request, + ); + return Either::Left(Either::Right(std::iter::once((peer, request)))); + } + } + return Either::Left(Either::Left(std::iter::empty())) + } if self.pending_requests.is_empty() || self.state_sync.is_some() { - return Either::Left(std::iter::empty()) + return Either::Left(Either::Left(std::iter::empty())) } if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { trace!(target: "sync", "Too many blocks in the queue."); - return Either::Left(std::iter::empty()) + return Either::Left(Either::Left(std::iter::empty())) } let major_sync = self.status().state == SyncState::Downloading; let attrs = self.required_block_attributes(); @@ -864,14 +919,14 @@ impl ChainSync { /// Get a state request, if any pub fn state_request(&mut self) -> Option<(PeerId, StateRequest)> { + if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { + // Only one pending state request is allowed. + return None; + } if let Some(sync) = &self.state_sync { if sync.is_complete() { return None; } - if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { - // Only one pending state request is allowed. - return None; - } for (id, peer) in self.peers.iter_mut() { if peer.state.is_available() && peer.common_number >= sync.target_block_num() { trace!(target: "sync", "New StateRequest for {}", id); @@ -881,6 +936,17 @@ impl ChainSync { } } } + if let Some(sync) = &self.warp_sync { + if let Some(request) = sync.next_state_request() { + for (id, peer) in self.peers.iter_mut() { + if peer.state.is_available() && peer.best_number >= sync.target_block_number() { + trace!(target: "sync", "New StateRequest for {}", id); + peer.state = PeerSyncState::DownloadingState; + return Some((id.clone(), request)) + } + } + } + } None } @@ -897,6 +963,17 @@ impl ChainSync { request: Option>, response: BlockResponse ) -> Result, BadPeer> { + if let Some(sync) = &mut self.warp_sync { + let result = sync.import_block(response); + return match result { + warp::ImportResult::Continue(_) => { + trace!(target: "sync", "Successfully imported warp header from {}", who); + Ok(OnBlockData::Ignore) + }, + warp::ImportResult::BadResponse => Err(BadPeer(who.clone(), rep::NO_BLOCK)), + warp::ImportResult::Import(..) => Ok(OnBlockData::Ignore), + } + } self.downloaded_blocks += response.blocks.len(); let new_blocks: Vec> = if let Some(peer) = self.peers.get_mut(who) { @@ -1098,6 +1175,15 @@ impl ChainSync { response.proof.len(), ); sync.import(response) + } else if let Some(sync) = &mut self.warp_sync { + debug!( + target: "sync", + "Importing state data from {} with {} keys, {} proof nodes.", + who, + response.entries.len(), + response.proof.len(), + ); + sync.import_state(response) } else { debug!(target: "sync", "Ignored obsolete state response from {}", who); return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)); @@ -1297,6 +1383,17 @@ impl ChainSync { self.mode = SyncMode::Full; output.extend(self.restart()); } + let warp_sync_complete = self.state_sync.as_ref().map_or(false, |s| s.target() == hash); + if warp_sync_complete { + info!( + target: "sync", + "Warp sync is complete ({} MiB), restarting block sync.", + self.warp_sync.as_ref().map_or(0, |s| s.progress().size / (1024 * 1024)), + ); + self.warp_sync = None; + self.mode = SyncMode::Full; + output.extend(self.restart()); + } }, Err(BlockImportError::IncompleteHeader(who)) => { if let Some(peer) = who { @@ -1342,6 +1439,7 @@ impl ChainSync { e @ Err(BlockImportError::Other(_)) => { warn!(target: "sync", "💔 Error importing block {:?}: {:?}", hash, e); self.state_sync = None; + self.warp_sync = None; output.extend(self.restart()); }, Err(BlockImportError::Cancelled) => {} diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs new file mode 100644 index 0000000000000..96efea9a7105c --- /dev/null +++ b/client/network/src/protocol/sync/warp.rs @@ -0,0 +1,150 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2021 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 . + +use std::sync::Arc; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor}; +use crate::schema::v1::{StateRequest, StateResponse}; +use crate::chain::Client; +use crate::protocol::message; +use super::state::StateSync; +use crate::StateDownloadProgress; +pub use super::state::ImportResult; + +/// Warp sync support. + +enum Phase { + Block, + State(StateSync), +} + +/// Warp sync state machine. Accumulates target header, warp proofs, state. +pub struct WarpSync { + target_hash: B::Hash, + target_num: NumberFor, + phase: Phase, + client: Arc>, +} + +impl WarpSync { + /// Create a new instance. + pub fn new(client: Arc>, target_hash: B::Hash, target_num: NumberFor) -> Self { + WarpSync { + client, + target_hash, + target_num, + phase: Phase::Block, + } + } + + /// Validate and import a state reponse. + pub fn import_state(&mut self, response: StateResponse) -> ImportResult { + match &mut self.phase { + Phase::Block => { + log::debug!( + target: "sync", + "Unexpected state response", + ); + return ImportResult::BadResponse; + } + Phase::State(sync) => sync.import(response) + } + } + + /// Validate and import a block reponse. + pub fn import_block(&mut self, mut response: message::BlockResponse) -> ImportResult { + match &mut self.phase { + Phase::Block => { + if response.blocks.len() != 1 { + log::debug!( target: "sync", "Bad block response"); + return ImportResult::BadResponse; + } + let header = match response.blocks.pop().and_then(|r| r.header) { + Some(header) => header, + None => { + log::debug!( target: "sync", "No header in the response"); + return ImportResult::BadResponse; + } + }; + if header.hash() != self.target_hash { + log::debug!( target: "sync", "Bad header in the response"); + return ImportResult::BadResponse; + } + let state_sync = StateSync::new(self.client.clone(), header, false); + let request = state_sync.next_request(); + self.phase = Phase::State(state_sync); + log::debug!( target: "sync", "Warp header received."); + ImportResult::Continue(request) + } + Phase::State(_) => { + log::debug!( + target: "sync", + "Unexpected block response", + ); + ImportResult::BadResponse + } + } + } + + /// Produce next state request. + pub fn next_block_request(&self) -> Option> { + match &self.phase { + Phase::Block => Some(message::generic::BlockRequest { + id: 0, + fields: message::BlockAttributes::HEADER, + from: message::FromBlock::Hash(self.target_hash), + to: None, + direction: message::Direction::Ascending, + max: Some(1) + }), + Phase::State(_) => None, + } + } + + /// Produce next state request. + pub fn next_state_request(&self) -> Option { + match &self.phase { + Phase::Block => None, + Phase::State(sync) => Some(sync.next_request()) + } + } + + /// Return target block number. + pub fn target_block_number(&self) -> NumberFor { + self.target_num + } + + /// Check if the state is complete. + pub fn is_complete(&self) -> bool { + match &self.phase { + Phase::Block => false, + Phase::State(sync) => sync.is_complete(), + } + } + + /// Returns state sync estimated progress (percentage, bytes) + pub fn progress(&self) -> StateDownloadProgress { + match &self.phase { + Phase::Block => StateDownloadProgress { + percentage: 0, + size: 0, + }, + Phase::State(sync) => sync.progress(), + } + } +} + From dd80234ed2f81abd111bf5b90291e18f18ee6232 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 25 Jun 2021 14:50:28 +0200 Subject: [PATCH 02/20] BABE & GRANDPA recovery --- bin/node-template/runtime/src/lib.rs | 4 + bin/node/runtime/src/lib.rs | 4 + client/cli/src/arg_enums.rs | 3 + client/consensus/aura/src/import_queue.rs | 37 +++-- client/consensus/babe/src/lib.rs | 132 ++++++++++++++---- client/consensus/babe/src/tests.rs | 10 +- client/consensus/babe/src/verification.rs | 2 + client/consensus/epochs/src/lib.rs | 33 ++++- client/consensus/manual-seal/src/lib.rs | 19 +-- client/consensus/pow/src/lib.rs | 26 ++-- client/db/src/cache/list_cache.rs | 1 + client/db/src/lib.rs | 33 +++-- client/finality-grandpa/src/import.rs | 84 ++++++++++- client/finality-grandpa/src/lib.rs | 6 +- client/finality-grandpa/src/tests.rs | 4 + client/informant/src/lib.rs | 4 +- client/network/src/gossip/tests.rs | 16 +-- client/network/src/protocol/sync.rs | 33 ++++- client/network/src/protocol/sync/warp.rs | 16 ++- client/network/src/service/tests.rs | 16 +-- client/network/test/src/lib.rs | 33 ++--- client/rpc/src/state/tests.rs | 8 +- client/service/src/builder.rs | 3 +- client/service/src/client/client.rs | 35 +++-- client/service/test/src/client/mod.rs | 9 ++ .../consensus/common/src/block_import.rs | 7 + .../consensus/common/src/import_queue.rs | 40 +++--- .../common/src/import_queue/basic_queue.rs | 7 +- primitives/finality-grandpa/src/lib.rs | 5 +- test-utils/runtime/src/lib.rs | 4 + 30 files changed, 414 insertions(+), 220 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index e89d7f28be220..fce55097e1fa4 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -403,6 +403,10 @@ impl_runtime_apis! { Grandpa::grandpa_authorities() } + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + fn submit_report_equivocation_unsigned_extrinsic( _equivocation_proof: fg_primitives::EquivocationProof< ::Hash, diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index fd7fd4213366f..1133e2bafc17a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1308,6 +1308,10 @@ impl_runtime_apis! { Grandpa::grandpa_authorities() } + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + fn submit_report_equivocation_unsigned_extrinsic( equivocation_proof: fg_primitives::EquivocationProof< ::Hash, diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 1bca67e782a3b..51282e6a07e49 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -243,6 +243,8 @@ arg_enum! { Fast, // Download blocks without executing them. Download latest state without proofs. FastUnsafe, + // Download the latest state + Warp, } } @@ -252,6 +254,7 @@ impl Into for SyncMode { SyncMode::Full => sc_network::config::SyncMode::Full, SyncMode::Fast => sc_network::config::SyncMode::Fast { skip_proofs: false }, SyncMode::FastUnsafe => sc_network::config::SyncMode::Fast { skip_proofs: true }, + SyncMode::Warp => sc_network::config::SyncMode::Warp, } } } diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index c3faa5382686e..0f5a2ac114d96 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -27,7 +27,7 @@ use prometheus_endpoint::Registry; use codec::{Encode, Decode, Codec}; use sp_consensus::{ BlockImport, CanAuthorWith, ForkChoiceStrategy, BlockImportParams, - BlockOrigin, Error as ConsensusError, + Error as ConsensusError, import_queue::{ Verifier, BasicQueue, DefaultImportQueue, BoxJustificationImport, }, @@ -35,7 +35,7 @@ use sp_consensus::{ use sc_client_api::{BlockOf, UsageProvider, backend::AuxStore}; use sp_blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, ProvideCache, HeaderBackend}; use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_runtime::{generic::{BlockId, OpaqueDigestItemId}, Justifications}; +use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor}; use sp_api::ProvideRuntimeApi; use sp_core::crypto::Pair; @@ -205,13 +205,10 @@ impl Verifier for AuraVerifier w { async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - mut body: Option>, + mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let hash = header.hash(); - let parent_hash = *header.parent_hash(); + let hash = block.header.hash(); + let parent_hash = *block.header.parent_hash(); let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash)) .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; @@ -234,7 +231,7 @@ impl Verifier for AuraVerifier w let checked_header = check_header::( &self.client, slot_now + 1, - header, + block.header, hash, &authorities[..], self.check_for_equivocation, @@ -244,8 +241,8 @@ impl Verifier for AuraVerifier w // if the body is passed through, we need to use the runtime // to check that the internally-set timestamp in the inherents // actually matches the slot set in the seal. - if let Some(inner_body) = body.take() { - let block = B::new(pre_header.clone(), inner_body); + if let Some(inner_body) = block.body.take() { + let new_block = B::new(pre_header.clone(), inner_body); inherent_data.aura_replace_inherent_data(slot); @@ -259,15 +256,15 @@ impl Verifier for AuraVerifier w .map_err(|e| format!("{:?}", e))? { self.check_inherents( - block.clone(), + new_block.clone(), BlockId::Hash(parent_hash), inherent_data, create_inherent_data_providers, ).await.map_err(|e| e.to_string())?; } - let (_, inner_body) = block.deconstruct(); - body = Some(inner_body); + let (_, inner_body) = new_block.deconstruct(); + block.body = Some(inner_body); } trace!(target: "aura", "Checked {:?}; importing.", pre_header); @@ -292,14 +289,12 @@ impl Verifier for AuraVerifier w _ => None, }); - let mut import_block = BlockImportParams::new(origin, pre_header); - import_block.post_digests.push(seal); - import_block.body = body; - import_block.justifications = justifications; - import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain); - import_block.post_hash = Some(hash); + block.header = pre_header; + block.post_digests.push(seal); + block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + block.post_hash = Some(hash); - Ok((import_block, maybe_keys)) + Ok((block, maybe_keys)) } CheckedHeader::Deferred(a, b) => { debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 61b58bf1b5999..9c7147d696fb9 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -111,7 +111,6 @@ use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; use sp_runtime::{ generic::{BlockId, OpaqueDigestItemId}, traits::{Block as BlockT, DigestItemFor, Header, Zero}, - Justifications, }; pub use sc_consensus_slots::SlotProportion; @@ -180,6 +179,19 @@ impl EpochT for Epoch { } } +impl From for Epoch { + fn from(epoch: sp_consensus_babe::Epoch) -> Self { + Epoch { + epoch_index: epoch.epoch_index, + start_slot: epoch.start_slot, + duration: epoch.duration, + authorities: epoch.authorities, + randomness: epoch.randomness, + config: epoch.config, + } + } +} + impl Epoch { /// Create the genesis epoch (epoch #0). This is defined to start at the slot of /// the first block, so that has to be provided. @@ -1102,24 +1114,27 @@ where { async fn verify( &mut self, - origin: BlockOrigin, - header: Block::Header, - justifications: Option, - mut body: Option>, + mut block: BlockImportParams, ) -> BlockVerificationResult { trace!( target: "babe", "Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}", - origin, - header, - justifications, - body, + block.origin, + block.header, + block.justifications, + block.body, ); - let hash = header.hash(); - let parent_hash = *header.parent_hash(); + let hash = block.header.hash(); + let parent_hash = *block.header.parent_hash(); + + if block.with_state() { + // When importting whole state we don't calculate epoch descriptor, but rater + // read it from the state after import. + return Ok((block, Default::default())) + } - debug!(target: "babe", "We have {:?} logs in this header", header.digest().logs().len()); + debug!(target: "babe", "We have {:?} logs in this header", block.header.digest().logs().len()); let create_inherent_data_providers = self .create_inherent_data_providers @@ -1132,7 +1147,7 @@ where let parent_header_metadata = self.client.header_metadata(parent_hash) .map_err(Error::::FetchParentHeader)?; - let pre_digest = find_pre_digest::(&header)?; + let pre_digest = find_pre_digest::(&block.header)?; let (check_header, epoch_descriptor) = { let epoch_changes = self.epoch_changes.shared_data(); let epoch_descriptor = epoch_changes.epoch_descriptor_for_child_of( @@ -1151,7 +1166,7 @@ where // We add one to the current slot to allow for some small drift. // FIXME #1019 in the future, alter this queue to allow deferring of headers let v_params = verification::VerificationParams { - header: header.clone(), + header: block.header.clone(), pre_digest: Some(pre_digest), slot_now: slot_now + 1, epoch: viable_epoch.as_ref(), @@ -1172,9 +1187,9 @@ where if let Err(err) = self.check_and_report_equivocation( slot_now, slot, - &header, + &block.header, &verified_info.author, - &origin, + &block.origin, ).await { warn!(target: "babe", "Error checking/reporting BABE equivocation: {:?}", err); } @@ -1182,21 +1197,21 @@ where // if the body is passed through, we need to use the runtime // to check that the internally-set timestamp in the inherents // actually matches the slot set in the seal. - if let Some(inner_body) = body.take() { + if let Some(inner_body) = block.body { let mut inherent_data = create_inherent_data_providers.create_inherent_data() .map_err(Error::::CreateInherents)?; inherent_data.babe_replace_inherent_data(slot); - let block = Block::new(pre_header.clone(), inner_body); + let new_block = Block::new(pre_header.clone(), inner_body); self.check_inherents( - block.clone(), + new_block.clone(), BlockId::Hash(parent_hash), inherent_data, create_inherent_data_providers, ).await?; - let (_, inner_body) = block.deconstruct(); - body = Some(inner_body); + let (_, inner_body) = new_block.deconstruct(); + block.body = Some(inner_body); } trace!(target: "babe", "Checked {:?}; importing.", pre_header); @@ -1207,17 +1222,15 @@ where "pre_header" => ?pre_header, ); - let mut import_block = BlockImportParams::new(origin, pre_header); - import_block.post_digests.push(verified_info.seal); - import_block.body = body; - import_block.justifications = justifications; - import_block.intermediates.insert( + block.header = pre_header; + block.post_digests.push(verified_info.seal); + block.intermediates.insert( Cow::from(INTERMEDIATE_KEY), Box::new(BabeIntermediate:: { epoch_descriptor }) as Box<_>, ); - import_block.post_hash = Some(hash); + block.post_hash = Some(hash); - Ok((import_block, Default::default())) + Ok((block, Default::default())) } CheckedHeader::Deferred(a, b) => { debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); @@ -1275,6 +1288,63 @@ impl BabeBlockImport { } } +impl BabeBlockImport where + Block: BlockT, + Inner: BlockImport> + Send + Sync, + Inner::Error: Into, + Client: HeaderBackend + HeaderMetadata + + AuxStore + ProvideRuntimeApi + ProvideCache + Send + Sync, + Client::Api: BabeApi + ApiExt, +{ + /// Import whole state after warp sync. + // This function makes multiple transactions to the DB. If one of them fails we may + // end up in an inconsistent state and have to resync. + async fn import_state( + &mut self, + mut block: BlockImportParams>, + new_cache: HashMap>, + ) -> Result { + let hash = block.post_hash(); + let parent_hash = *block.header.parent_hash(); + let number = *block.header.number(); + + block.fork_choice = Some(ForkChoiceStrategy::Custom(true)); + // Reset block weight + aux_schema::write_block_weight( + hash, + 0, + |values| block.auxiliary.extend( + values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) + ), + ); + + // First make the client import the state. + let import_result = self.inner.import_block(block, new_cache).await; + let aux = match import_result { + Ok(ImportResult::Imported(aux)) => aux, + Ok(r) => + return Err(ConsensusError::ClientImport(format!("Unexpected import result: {:?}", r))), + Err(r) => return Err(r.into()), + }; + + // Read epoch info from the imported state. + let block_id = BlockId::hash(hash); + let current_epoch = self.client.runtime_api().current_epoch(&block_id) + .map_err(|e| ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()))?; + let next_epoch = self.client.runtime_api().next_epoch(&block_id) + .map_err(|e| ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()))?; + + let mut epoch_changes = self.epoch_changes.shared_data_locked(); + epoch_changes.reset(parent_hash, hash, number, current_epoch.into(), next_epoch.into()); + aux_schema::write_epoch_changes::( + &*epoch_changes, + |insert| self.client.insert_aux(insert, []) + ).map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + + Ok(ImportResult::Imported(aux)) + } +} + #[async_trait::async_trait] impl BlockImport for BabeBlockImport where Block: BlockT, @@ -1300,7 +1370,7 @@ impl BlockImport for BabeBlockImport { // When re-importing existing block strip away intermediates. - let _ = block.take_intermediate::>(INTERMEDIATE_KEY)?; + let _ = block.take_intermediate::>(INTERMEDIATE_KEY); block.fork_choice = Some(ForkChoiceStrategy::Custom(false)); return self.inner.import_block(block, new_cache).await.map_err(Into::into) }, @@ -1308,6 +1378,10 @@ impl BlockImport for BabeBlockImport return Err(ConsensusError::ClientImport(e.to_string())), } + if matches!(block.state_action, StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_))) { + return self.import_state(block, new_cache).await; + } + let pre_digest = find_pre_digest::(&block.header) .expect("valid babe headers must contain a predigest; \ header has been already verified; qed"); diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 3392ffade98ee..88a3e49bccaa8 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -231,7 +231,6 @@ pub struct BabeTestNet { } type TestHeader = ::Header; -type TestExtrinsic = ::Extrinsic; type TestSelectChain = substrate_test_runtime_client::LongestChain< substrate_test_runtime_client::Backend, @@ -260,14 +259,11 @@ impl Verifier for TestVerifier { /// presented to the User in the logs. async fn verify( &mut self, - origin: BlockOrigin, - mut header: TestHeader, - justifications: Option, - body: Option>, + mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { // apply post-sealing mutations (i.e. stripping seal, if desired). - (self.mutator)(&mut header, Stage::PostSeal); - self.inner.verify(origin, header, justifications, body).await + (self.mutator)(&mut block.header, Stage::PostSeal); + self.inner.verify(block).await } } diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 469286f5110d7..22b88ddd9373c 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -219,6 +219,7 @@ fn check_secondary_plain_header( let author = &epoch.authorities[pre_digest.authority_index as usize].0; + println!("CHECKING EPOCH {}, slot={}", epoch.epoch_index, pre_digest.slot); if expected_author != author { return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())); } @@ -247,6 +248,7 @@ fn check_secondary_vrf_header( let author = &epoch.authorities[pre_digest.authority_index as usize].0; + println!("CHECKING SEC EPOCH {}, slot={}", epoch.epoch_index, pre_digest.slot); if expected_author != author { return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())); } diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 98a3e83530510..75fe54d84fc81 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -20,7 +20,7 @@ pub mod migration; -use std::{ops::Add, collections::BTreeMap, borrow::{Borrow, BorrowMut}}; +use std::{ops::{Add, Sub}, collections::BTreeMap, borrow::{Borrow, BorrowMut}}; use codec::{Encode, Decode}; use fork_tree::ForkTree; use sc_client_api::utils::is_descendent_of; @@ -230,7 +230,7 @@ impl ViableEpochDescriptor { } /// Persisted epoch stored in EpochChanges. -#[derive(Clone, Encode, Decode, Debug)] +#[derive(Clone, Encode, Decode)] pub enum PersistedEpoch { /// Genesis persisted epoch data. epoch_0, epoch_1. Genesis(E, E), @@ -250,7 +250,7 @@ impl<'a, E: Epoch> From<&'a PersistedEpoch> for PersistedEpochHeader { } /// Persisted epoch header stored in ForkTree. -#[derive(Encode, Decode, PartialEq, Eq)] +#[derive(Encode, Decode, PartialEq, Eq, Debug)] pub enum PersistedEpochHeader { /// Genesis persisted epoch header. epoch_0, epoch_1. Genesis(EpochHeader, EpochHeader), @@ -323,7 +323,7 @@ impl Default for EpochChanges where impl EpochChanges where Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy, - Number: Ord + One + Zero + Add + Copy, + Number: Ord + One + Zero + Add + Sub + Copy, { /// Create a new epoch change. pub fn new() -> Self { @@ -641,6 +641,31 @@ impl EpochChanges where pub fn tree(&self) -> &ForkTree> { &self.inner } + + /// Reset to explicit specified pair of epochs. + pub fn reset(&mut self, parent_hash: Hash, hash: Hash, number: Number, current: E, next: E) { + self.inner = ForkTree::new(); + self.epochs.clear(); + let persisted = PersistedEpoch::Regular(current); + let header = PersistedEpochHeader::from(&persisted); + let _res = self.inner.import( + parent_hash, + number - One::one(), + header, + &|_, _| Ok(false) as Result>, + ); + self.epochs.insert((parent_hash, number - One::one()), persisted); + + let persisted = PersistedEpoch::Regular(next); + let header = PersistedEpochHeader::from(&persisted); + let _res = self.inner.import( + hash, + number, + header, + &|_, _| Ok(true) as Result>, + ); + self.epochs.insert((hash, number), persisted); + } } /// Type alias to produce the epoch-changes tree from a block type. diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 1e8c69a752ca2..737d6543449bc 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -22,12 +22,12 @@ use futures::prelude::*; use sp_consensus::{ Environment, Proposer, SelectChain, BlockImport, - ForkChoiceStrategy, BlockImportParams, BlockOrigin, + ForkChoiceStrategy, BlockImportParams, import_queue::{Verifier, BasicQueue, CacheKeyId, BoxBlockImport}, }; use sp_blockchain::HeaderBackend; use sp_inherents::CreateInherentDataProviders; -use sp_runtime::{traits::Block as BlockT, Justifications, ConsensusEngineId}; +use sp_runtime::{traits::Block as BlockT, ConsensusEngineId}; use sc_client_api::backend::{Backend as ClientBackend, Finalizer}; use sc_transaction_pool::{ChainApi, Pool}; use std::{sync::Arc, marker::PhantomData}; @@ -59,18 +59,11 @@ struct ManualSealVerifier; impl Verifier for ManualSealVerifier { async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - body: Option>, + mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let mut import_params = BlockImportParams::new(origin, header); - import_params.justifications = justifications; - import_params.body = body; - import_params.finalized = false; - import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); - - Ok((import_params, None)) + block.finalized = false; + block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + Ok((block, None)) } } diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index e71726564ebe5..11a97e105ef86 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -52,14 +52,14 @@ use parking_lot::Mutex; use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents}; use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId}; use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_runtime::{Justifications, RuntimeString}; +use sp_runtime::RuntimeString; use sp_runtime::generic::{BlockId, Digest, DigestItem}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_api::ProvideRuntimeApi; use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_consensus::{ - BlockImportParams, BlockOrigin, ForkChoiceStrategy, SyncOracle, Environment, Proposer, + BlockImportParams, ForkChoiceStrategy, SyncOracle, Environment, Proposer, SelectChain, Error as ConsensusError, CanAuthorWith, BlockImport, BlockCheckParams, ImportResult, }; use sp_consensus::import_queue::{ @@ -465,29 +465,23 @@ impl Verifier for PowVerifier where { async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - body: Option>, + mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let hash = header.hash(); - let (checked_header, seal) = self.check_header(header)?; + let hash = block.header.hash(); + let (checked_header, seal) = self.check_header(block.header)?; let intermediate = PowIntermediate:: { difficulty: None, }; - - let mut import_block = BlockImportParams::new(origin, checked_header); - import_block.post_digests.push(seal); - import_block.body = body; - import_block.justifications = justifications; - import_block.intermediates.insert( + block.header = checked_header; + block.post_digests.push(seal); + block.intermediates.insert( Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_> ); - import_block.post_hash = Some(hash); + block.post_hash = Some(hash); - Ok((import_block, None)) + Ok((block, None)) } } diff --git a/client/db/src/cache/list_cache.rs b/client/db/src/cache/list_cache.rs index 341105b16a5b3..fbbb5ee7f4b91 100644 --- a/client/db/src/cache/list_cache.rs +++ b/client/db/src/cache/list_cache.rs @@ -296,6 +296,7 @@ impl> ListCache let prev_operation = operations.operations.last(); debug_assert!( entry_type != EntryType::Final || + self.unfinalized.is_empty() || self.best_finalized_block.hash == parent.hash || match prev_operation { Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 024f2e5f4e649..0585f678ca467 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -664,7 +664,7 @@ impl HeaderMetadata for BlockchainDb { header_metadata.clone(), ); header_metadata - }).ok_or_else(|| ClientError::UnknownBlock(format!("header not found in db: {}", hash))) + }).ok_or_else(|| ClientError::UnknownBlock(format!("Header was not found in the database: {:?}", hash))) }, Ok) } @@ -1176,9 +1176,11 @@ impl Backend { let mut retracted = Vec::default(); let meta = self.blockchain.meta.read(); + let parent_exists = self.blockchain.status(BlockId::Hash(route_to))? + == sp_blockchain::BlockStatus::InChain; - // cannot find tree route with empty DB. - if meta.best_hash != Default::default() { + // Cannot find tree route with empty DB or when imported a detached block. + if meta.best_hash != Default::default() && parent_exists { let tree_route = sp_blockchain::tree_route( &self.blockchain, meta.best_hash, @@ -1235,10 +1237,12 @@ impl Backend { last_finalized: Option, ) -> ClientResult<()> { let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); - if *header.parent_hash() != last_finalized { - return Err(::sp_blockchain::Error::NonSequentialFinalization( - format!("Last finalized {:?} not parent of {:?}", last_finalized, header.hash()), - ).into()); + if last_finalized != self.blockchain.meta.read().genesis_hash { + if *header.parent_hash() != last_finalized { + return Err(::sp_blockchain::Error::NonSequentialFinalization( + format!("Last finalized {:?} not parent of {:?}", last_finalized, header.hash()), + ).into()); + } } Ok(()) } @@ -1521,7 +1525,6 @@ impl Backend { let cache = operation.old_state.into_cache_changes(); if finalized { - // TODO: ensure best chain contains this block. self.ensure_sequential_finalization(header, Some(last_finalized_hash))?; self.note_finalized( &mut transaction, @@ -1554,14 +1557,14 @@ impl Backend { )?; if !children.contains(&hash) { children.push(hash); + children::write_children( + &mut transaction, + columns::META, + meta_keys::CHILDREN_PREFIX, + parent_hash, + children, + ); } - children::write_children( - &mut transaction, - columns::META, - meta_keys::CHILDREN_PREFIX, - parent_hash, - children, - ); meta_updates.push(MetaUpdate { hash, diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index ebb26a28c3485..a8f71fd3e8539 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -19,7 +19,7 @@ use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use log::debug; -use parity_scale_codec::Encode; +use parity_scale_codec::{Encode, Decode}; use sc_client_api::{backend::Backend, utils::is_descendent_of}; use sc_consensus::shared_data::{SharedDataLocked, SharedDataLockedUpgradable}; @@ -28,13 +28,14 @@ use sp_api::TransactionFor; use sp_blockchain::{well_known_cache_keys, BlockStatus}; use sp_consensus::{ BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, Error as ConsensusError, - ImportResult, JustificationImport, SelectChain, + ImportResult, JustificationImport, SelectChain, StateAction, }; -use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; +use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GrandpaApi, GRANDPA_ENGINE_ID}; use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero}; use sp_runtime::Justification; use sp_utils::mpsc::TracingUnboundedSender; +use sp_core::hashing::twox_128; use crate::{ authorities::{AuthoritySet, DelayKind, PendingChange, SharedAuthoritySet}, @@ -42,6 +43,7 @@ use crate::{ justification::GrandpaJustification, notification::GrandpaJustificationSender, ClientForGrandpa, CommandOrError, Error, NewAuthoritySet, VoterCommand, + AuthoritySetChanges, }; /// A block-import handler for GRANDPA. @@ -232,6 +234,10 @@ where DigestFor: Encode, BE: Backend, Client: ClientForGrandpa, + Client::Api: GrandpaApi, + for<'a> &'a Client: + BlockImport>, + TransactionFor: 'static, { // check for a new authority set change. fn check_new_change( @@ -427,6 +433,73 @@ where do_pause, }) } + + /// Read current set id form a given state. + fn current_set_id(&self, id: &BlockId) -> Result { + match self.inner.runtime_api().current_set_id(&id) { + Ok(set_id) => Ok(set_id), + Err(sp_api::ApiError::Application(_)) => { + // The new API is not supported in this runtime. Try reading directly from storage. + // This code may be removed once warp sync to an old runtime is no longer needed. + for prefix in ["GrandpaFinality", "Grandpa"] { + let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat(); + if let Ok(Some(id)) = self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec())) { + if let Ok(id) = SetId::decode(&mut id.0.as_ref()) { + return Ok(id) + } + } + } + Err(ConsensusError::ClientImport("Unable to read retrieve current set id.".into())) + } + Err(e) => Err(ConsensusError::ClientImport(e.to_string())), + } + } + + async fn import_state( + &mut self, + mut block: BlockImportParams>, + new_cache: HashMap>, + ) -> Result { + let hash = block.post_hash(); + let number = *block.header.number(); + // Force imported state finality. + block.finalized = true; + let import_result = (&*self.inner).import_block(block, new_cache).await; + match import_result { + Ok(ImportResult::Imported(aux)) => { + // Read authority list and set id from the state. + self.authority_set_hard_forks.clear(); + let block_id = BlockId::hash(hash); + let authorities = self.inner.runtime_api().grandpa_authorities(&block_id) + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + let set_id = self.current_set_id(&block_id)?; + let authority_set = AuthoritySet::new( + authorities.clone(), + set_id, + fork_tree::ForkTree::new(), + Vec::new(), + AuthoritySetChanges::empty(), + ).ok_or_else(|| ConsensusError::ClientImport("Invalid authority list".into()))?; + *self.authority_set.inner_locked() = authority_set.clone(); + + crate::aux_schema::update_authority_set::( + &authority_set, + None, + |insert| self.inner.insert_aux(insert, []) + ).map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + let new_set = NewAuthoritySet { + canon_number: number, + canon_hash: hash, + set_id, + authorities, + }; + let _ = self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new_set)); + Ok(ImportResult::Imported(aux)) + } + Ok(r) => Ok(r), + Err(e) => Err(ConsensusError::ClientImport(e.to_string())), + } + } } #[async_trait::async_trait] @@ -436,6 +509,7 @@ where DigestFor: Encode, BE: Backend, Client: ClientForGrandpa, + Client::Api: GrandpaApi, for<'a> &'a Client: BlockImport>, TransactionFor: 'static, @@ -464,6 +538,10 @@ where Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } + if matches!(block.state_action, StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_))) { + return self.import_state(block, new_cache).await; + } + // on initial sync we will restrict logging under info to avoid spam. let initial_sync = block.origin == BlockOrigin::NetworkInitialSync; diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 6c3f0f6af37a8..9597368149684 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -63,7 +63,7 @@ use futures::{ use log::{debug, error, info}; use sc_client_api::{ backend::{AuxStore, Backend}, - LockImportRun, BlockchainEvents, CallExecutor, + LockImportRun, BlockchainEvents, CallExecutor, StorageProvider, ExecutionStrategy, Finalizer, TransactionFor, ExecutorProvider, }; use parity_scale_codec::{Decode, Encode}; @@ -341,6 +341,7 @@ pub trait ClientForGrandpa: + HeaderMetadata + HeaderBackend + BlockchainEvents + ProvideRuntimeApi + ExecutorProvider + BlockImport, Error = sp_consensus::Error> + + StorageProvider where BE: Backend, Block: BlockT, @@ -353,7 +354,8 @@ impl ClientForGrandpa for T T: LockImportRun + Finalizer + AuxStore + HeaderMetadata + HeaderBackend + BlockchainEvents + ProvideRuntimeApi + ExecutorProvider - + BlockImport, Error = sp_consensus::Error>, + + BlockImport, Error = sp_consensus::Error> + + StorageProvider, {} /// Something that one can ask to do a block sync request. diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 725beec6a94b2..3a76573f4323e 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -205,6 +205,10 @@ sp_api::mock_impl_runtime_apis! { self.inner.genesis_authorities.clone() } + fn current_set_id(&self) -> SetId { + 0 + } + fn submit_report_equivocation_unsigned_extrinsic( _equivocation_proof: EquivocationProof, _key_owner_proof: OpaqueKeyOwnershipProof, diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index ef1533fb49f76..ef500d842f023 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -21,7 +21,7 @@ use ansi_term::Colour; use futures::prelude::*; use futures_timer::Delay; -use log::{info, trace, warn}; +use log::{info, trace, debug}; use parity_util_mem::MallocSizeOf; use sc_client_api::{BlockchainEvents, UsageProvider}; use sc_network::NetworkService; @@ -146,7 +146,7 @@ where Colour::White.bold().paint(format!("{}", ancestor.number)), ancestor.hash, ), Ok(_) => {}, - Err(e) => warn!("Error computing tree route: {}", e), + Err(e) => debug!("Error computing tree route: {}", e), } } } diff --git a/client/network/src/gossip/tests.rs b/client/network/src/gossip/tests.rs index bdef28f9bebe5..8e8d46da4cc8a 100644 --- a/client/network/src/gossip/tests.rs +++ b/client/network/src/gossip/tests.rs @@ -53,10 +53,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration) impl sp_consensus::import_queue::Verifier for PassThroughVerifier { async fn verify( &mut self, - origin: sp_consensus::BlockOrigin, - header: B::Header, - justifications: Option, - body: Option>, + mut block: sp_consensus::BlockImportParams, ) -> Result< ( sp_consensus::BlockImportParams, @@ -64,7 +61,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration) ), String, > { - let maybe_keys = header + let maybe_keys = block.header .digest() .log(|l| { l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura")) @@ -79,12 +76,9 @@ fn build_test_full_node(network_config: config::NetworkConfiguration) )] }); - let mut import = sp_consensus::BlockImportParams::new(origin, header); - import.body = body; - import.finalized = self.0; - import.justifications = justifications; - import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain); - Ok((import, maybe_keys)) + block.finalized = self.0; + block.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain); + Ok((block, maybe_keys)) } } diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index b85792f3223e1..2ad120fc07c94 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -295,6 +295,8 @@ pub enum PeerSyncState { DownloadingJustification(B::Hash), /// Downloading state. DownloadingState, + /// Downloading warp block. + DownloadingWarp, } impl PeerSyncState { @@ -631,15 +633,13 @@ impl ChainSync { if let SyncMode::Warp = &self.mode { // TODO: wait for 3 peers if self.warp_sync.is_none() { - let number = best_number; - let hash = best_hash; + let number = best_number - 16u32.into(); log::debug!( target: "sync", - "Starting warp state sync for #{} ({})", + "Starting warp state sync for #{}", number, - hash, ); - self.warp_sync = Some(WarpSync::new(self.client.clone(), hash, number)); + self.warp_sync = Some(WarpSync::new(self.client.clone(), number)); } } // If we are at genesis, just start downloading. @@ -804,14 +804,19 @@ impl ChainSync { /// Get an iterator over all block requests of all peers. pub fn block_requests(&mut self) -> impl Iterator)> + '_ { if let Some(warp_sync) = &self.warp_sync { + if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarp) { + // Only one pending warp request is allowed. + return Either::Left(Either::Left(std::iter::empty())) + } if let Some(request) = warp_sync.next_block_request() { // Find a peer that can handle the request. - let peer = self.peers.iter().find_map(move |(id, peer)| { + let peer = self.peers.iter_mut().find_map(move |(id, peer)| { if !peer.state.is_available() { return None } if peer.best_number >= warp_sync.target_block_number() { + peer.state = PeerSyncState::DownloadingWarp; Some((id, peer.best_number)) } else { None @@ -938,6 +943,9 @@ impl ChainSync { } if let Some(sync) = &self.warp_sync { if let Some(request) = sync.next_state_request() { + if sync.is_complete() { + return None; + } for (id, peer) in self.peers.iter_mut() { if peer.state.is_available() && peer.best_number >= sync.target_block_number() { trace!(target: "sync", "New StateRequest for {}", id); @@ -964,6 +972,9 @@ impl ChainSync { response: BlockResponse ) -> Result, BadPeer> { if let Some(sync) = &mut self.warp_sync { + if let Some(peer) = self.peers.get_mut(who) { + peer.state = PeerSyncState::Available; + } let result = sync.import_block(response); return match result { warp::ImportResult::Continue(_) => { @@ -1128,6 +1139,7 @@ impl ChainSync { PeerSyncState::Available | PeerSyncState::DownloadingJustification(..) | PeerSyncState::DownloadingState + | PeerSyncState::DownloadingWarp => Vec::new() } } else { @@ -1383,7 +1395,7 @@ impl ChainSync { self.mode = SyncMode::Full; output.extend(self.restart()); } - let warp_sync_complete = self.state_sync.as_ref().map_or(false, |s| s.target() == hash); + let warp_sync_complete = self.warp_sync.as_ref().map_or(false, |s| s.target_block_hash() == hash); if warp_sync_complete { info!( target: "sync", @@ -1926,6 +1938,13 @@ impl ChainSync { ); self.mode = SyncMode::Full; } + if matches!(self.mode, SyncMode::Warp) && info.finalized_state.is_some() { + log::warn!( + target: "sync", + "Can't use warp sync mode with a partially synced database. Reverting to full sync mode." + ); + self.mode = SyncMode::Full; + } self.import_existing = false; self.best_queued_hash = info.best_hash; self.best_queued_number = info.best_number; diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 96efea9a7105c..3157ac749123c 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -42,10 +42,10 @@ pub struct WarpSync { impl WarpSync { /// Create a new instance. - pub fn new(client: Arc>, target_hash: B::Hash, target_num: NumberFor) -> Self { + pub fn new(client: Arc>, target_num: NumberFor) -> Self { WarpSync { client, - target_hash, + target_hash: Default::default(), target_num, phase: Phase::Block, } @@ -80,10 +80,7 @@ impl WarpSync { return ImportResult::BadResponse; } }; - if header.hash() != self.target_hash { - log::debug!( target: "sync", "Bad header in the response"); - return ImportResult::BadResponse; - } + self.target_hash = header.hash(); let state_sync = StateSync::new(self.client.clone(), header, false); let request = state_sync.next_request(); self.phase = Phase::State(state_sync); @@ -106,7 +103,7 @@ impl WarpSync { Phase::Block => Some(message::generic::BlockRequest { id: 0, fields: message::BlockAttributes::HEADER, - from: message::FromBlock::Hash(self.target_hash), + from: message::FromBlock::Number(self.target_num), to: None, direction: message::Direction::Ascending, max: Some(1) @@ -123,6 +120,11 @@ impl WarpSync { } } + /// Return target block hash. + pub fn target_block_hash(&self) -> B::Hash { + self.target_hash.clone() + } + /// Return target block number. pub fn target_block_number(&self) -> NumberFor { self.target_num diff --git a/client/network/src/service/tests.rs b/client/network/src/service/tests.rs index 4a739e50628a5..77c3d412c401f 100644 --- a/client/network/src/service/tests.rs +++ b/client/network/src/service/tests.rs @@ -53,10 +53,7 @@ fn build_test_full_node(config: config::NetworkConfiguration) impl sp_consensus::import_queue::Verifier for PassThroughVerifier { async fn verify( &mut self, - origin: sp_consensus::BlockOrigin, - header: B::Header, - justifications: Option, - body: Option>, + mut block: sp_consensus::BlockImportParams, ) -> Result< ( sp_consensus::BlockImportParams, @@ -64,7 +61,7 @@ fn build_test_full_node(config: config::NetworkConfiguration) ), String, > { - let maybe_keys = header + let maybe_keys = block.header .digest() .log(|l| { l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura")) @@ -79,12 +76,9 @@ fn build_test_full_node(config: config::NetworkConfiguration) )] }); - let mut import = sp_consensus::BlockImportParams::new(origin, header); - import.body = body; - import.finalized = self.0; - import.justifications = justifications; - import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain); - Ok((import, maybe_keys)) + block.finalized = self.0; + block.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain); + Ok((block, maybe_keys)) } } diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index b6e8f897bb809..ab25e7f8b53ee 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -108,23 +108,16 @@ impl PassThroughVerifier { impl Verifier for PassThroughVerifier { async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - body: Option> + mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let maybe_keys = header.digest() + let maybe_keys = block.header.digest() .log(|l| l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura")) .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) ) .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); - let mut import = BlockImportParams::new(origin, header); - import.body = body; - import.finalized = self.finalized; - import.justifications = justifications; - import.fork_choice = Some(self.fork_choice.clone()); - - Ok((import, maybe_keys)) + block.finalized = self.finalized; + block.fork_choice = Some(self.fork_choice.clone()); + Ok((block, maybe_keys)) } } @@ -372,11 +365,10 @@ impl Peer where block.header.parent_hash, ); let header = block.header.clone(); + let mut import_block = BlockImportParams::new(origin, header.clone()); + import_block.body = if headers_only { None } else { Some(block.extrinsics) }; let (import_block, cache) = futures::executor::block_on(self.verifier.verify( - origin, - header.clone(), - None, - if headers_only { None } else { Some(block.extrinsics) }, + import_block, )).unwrap(); let cache = if let Some(cache) = cache { cache.into_iter().collect() @@ -612,13 +604,10 @@ struct VerifierAdapter { impl Verifier for VerifierAdapter { async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - body: Option> + block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let hash = header.hash(); - self.verifier.lock().await.verify(origin, header, justifications, body).await.map_err(|e| { + let hash = block.header.hash(); + self.verifier.lock().await.verify(block).await.map_err(|e| { self.failed_verifications.lock().insert(hash, e.clone()); e }) diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index e413827552c9d..9a224b57ead5a 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -366,7 +366,7 @@ fn should_query_storage() { Err(Error::InvalidBlockRange { from: format!("{:?}", genesis_hash), to: format!("{:?}", Some(random_hash1)), - details: format!("UnknownBlock: header not found in db: {}", random_hash1), + details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), }).map_err(|e| e.to_string()) ); @@ -382,7 +382,7 @@ fn should_query_storage() { Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), to: format!("{:?}", Some(genesis_hash)), - details: format!("UnknownBlock: header not found in db: {}", random_hash1), + details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), }).map_err(|e| e.to_string()), ); @@ -398,7 +398,7 @@ fn should_query_storage() { Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), to: format!("{:?}", Some(block2_hash)), // Best block hash. - details: format!("UnknownBlock: header not found in db: {}", random_hash1), + details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), }).map_err(|e| e.to_string()), ); @@ -414,7 +414,7 @@ fn should_query_storage() { Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), // First hash not found. to: format!("{:?}", Some(random_hash2)), - details: format!("UnknownBlock: header not found in db: {}", random_hash1), + details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), }).map_err(|e| e.to_string()), ); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index b0bffc3c4e12d..0806d9a64bca3 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -378,7 +378,8 @@ pub fn new_full_parts( offchain_worker_enabled : config.offchain_worker.enabled, offchain_indexing_api: config.offchain_worker.indexing_enabled, wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), - no_genesis: matches!(config.network.sync_mode, sc_network::config::SyncMode::Fast {..}), + no_genesis: matches!(config.network.sync_mode, sc_network::config::SyncMode::Fast {..}) + || matches!(config.network.sync_mode, sc_network::config::SyncMode::Warp), wasm_runtime_substitutes, }, )?; diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 4a998a12d2b7f..4fda314b33ec6 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -747,6 +747,8 @@ impl Client where { let parent_hash = import_headers.post().parent_hash().clone(); let status = self.backend.blockchain().status(BlockId::Hash(hash))?; + let parent_exists = self.backend.blockchain().status(BlockId::Hash(parent_hash))? + == blockchain::BlockStatus::InChain; match (import_existing, status) { (false, blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain), (false, blockchain::BlockStatus::Unknown) => {}, @@ -797,7 +799,6 @@ impl Client where if let Some(changes_trie_transaction) = changes_trie_tx { operation.op.update_changes_trie(changes_trie_transaction)?; } - Some((main_sc, child_sc)) } sp_consensus::StorageChanges::Import(changes) => { @@ -817,18 +818,6 @@ impl Client where } }; - // ensure parent block is finalized to maintain invariant that - // finality is called sequentially. - if finalized { - self.apply_finality_with_block_hash( - operation, - parent_hash, - None, - info.best_hash, - make_notifications, - )?; - } - operation.op.update_cache(new_cache); storage_changes @@ -849,7 +838,7 @@ impl Client where NewBlockState::Normal }; - let tree_route = if is_new_best && info.best_hash != parent_hash { + let tree_route = if is_new_best && info.best_hash != parent_hash && parent_exists { let route_from_best = sp_blockchain::tree_route( self.backend.blockchain(), info.best_hash, @@ -914,17 +903,17 @@ impl Client where let at = BlockId::Hash(*parent_hash); let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip); let (enact_state, storage_changes) = match (self.block_status(&at)?, state_action) { - (BlockStatus::Unknown, _) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)), (BlockStatus::KnownBad, _) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad)), - (_, StateAction::Skip) => (false, None), (BlockStatus::InChainPruned, StateAction::ApplyChanges(sp_consensus::StorageChanges::Changes(_))) => - return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), + return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), + (_, StateAction::ApplyChanges(changes)) => (true, Some(changes)), + (BlockStatus::Unknown, _) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)), + (_, StateAction::Skip) => (false, None), (BlockStatus::InChainPruned, StateAction::Execute) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), (BlockStatus::InChainPruned, StateAction::ExecuteIfPossible) => (false, None), (_, StateAction::Execute) => (true, None), (_, StateAction::ExecuteIfPossible) => (true, None), - (_, StateAction::ApplyChanges(changes)) => (true, Some(changes)), }; let storage_changes = match (enact_state, storage_changes, &import_block.body) { @@ -1875,7 +1864,14 @@ impl sp_consensus::BlockImport for &Client, ) -> Result { - let BlockCheckParams { hash, number, parent_hash, allow_missing_state, import_existing } = block; + let BlockCheckParams { + hash, + number, + parent_hash, + allow_missing_state, + import_existing, + allow_missing_parent, + } = block; // Check the block against white and black lists if any are defined // (i.e. fork blocks and bad blocks respectively) @@ -1921,6 +1917,7 @@ impl sp_consensus::BlockImport for &Client {}, + BlockStatus::Unknown if allow_missing_parent => {}, BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), BlockStatus::InChainPruned if allow_missing_state => {}, BlockStatus::InChainPruned => return Ok(ImportResult::MissingState), diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 9cd0e193fcd03..48d179b73fa1a 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1283,6 +1283,7 @@ fn import_with_justification() { false, ).unwrap().build().unwrap().block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); + client.finalize_block(BlockId::hash(a2.hash()), None).unwrap(); // A2 -> A3 let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3])); @@ -1659,6 +1660,7 @@ fn respects_block_rules() { number: 0, parent_hash: block_ok.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false)); @@ -1674,6 +1676,7 @@ fn respects_block_rules() { number: 0, parent_hash: block_not_ok.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; if record_only { @@ -1696,6 +1699,7 @@ fn respects_block_rules() { number: 1, parent_hash: block_ok.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; if record_only { @@ -1714,6 +1718,7 @@ fn respects_block_rules() { number: 1, parent_hash: block_not_ok.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; @@ -1778,6 +1783,7 @@ fn returns_status_for_pruned_blocks() { number: 0, parent_hash: a1.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; @@ -1813,6 +1819,7 @@ fn returns_status_for_pruned_blocks() { number: 1, parent_hash: a1.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; @@ -1845,6 +1852,7 @@ fn returns_status_for_pruned_blocks() { number: 2, parent_hash: a2.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; @@ -1879,6 +1887,7 @@ fn returns_status_for_pruned_blocks() { number: 0, parent_hash: b1.header().parent_hash().clone(), allow_missing_state: false, + allow_missing_parent: false, import_existing: false, }; assert_eq!( diff --git a/primitives/consensus/common/src/block_import.rs b/primitives/consensus/common/src/block_import.rs index 447ea5761f767..ac9fab0d11612 100644 --- a/primitives/consensus/common/src/block_import.rs +++ b/primitives/consensus/common/src/block_import.rs @@ -131,6 +131,8 @@ pub struct BlockCheckParams { pub parent_hash: Block::Hash, /// Allow importing the block skipping state verification if parent state is missing. pub allow_missing_state: bool, + /// Allow importing the block if parent block is missing. + pub allow_missing_parent: bool, /// Re-validate existing block. pub import_existing: bool, } @@ -321,6 +323,11 @@ impl BlockImportParams { .downcast_mut::() .ok_or(Error::InvalidIntermediate) } + + /// Check if this block contains state import action + pub fn with_state(&self) -> bool { + matches!(self.state_action, StateAction::ApplyChanges(StorageChanges::Import(_))) + } } /// Block import trait. diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index fba5b51e921ca..3d8de48d3af72 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -93,10 +93,7 @@ pub trait Verifier: Send + Sync { /// presented to the User in the logs. async fn verify( &mut self, - origin: BlockOrigin, - header: B::Header, - justifications: Option, - body: Option>, + block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String>; } @@ -202,7 +199,7 @@ pub(crate) async fn import_single_block_metered, Trans trace!(target: "sync", "Header {} has {:?} logs", block.hash, header.digest().logs().len()); let number = header.number().clone(); - let hash = header.hash(); + let hash = block.hash; let parent_hash = header.parent_hash().clone(); let import_handler = |import| { @@ -237,17 +234,29 @@ pub(crate) async fn import_single_block_metered, Trans parent_hash, allow_missing_state: block.allow_missing_state, import_existing: block.import_existing, + allow_missing_parent: block.state.is_some(), }).await)? { BlockImportResult::ImportedUnknown { .. } => (), r => return Ok(r), // Any other successful result means that the block is already imported. } let started = wasm_timer::Instant::now(); - let (mut import_block, maybe_keys) = verifier.verify( - block_origin, - header, - justifications, - block.body + + let mut import_block = BlockImportParams::new(block_origin, header); + import_block.body = block.body; + import_block.justifications = justifications; + import_block.post_hash = Some(hash); + import_block.import_existing = block.import_existing; + if let Some(state) = block.state { + import_block.state_action = StateAction::ApplyChanges(crate::StorageChanges::Import(state)); + } else if block.skip_execution { + import_block.state_action = StateAction::Skip; + } else if block.allow_missing_state { + import_block.state_action = StateAction::ExecuteIfPossible; + } + + let (import_block, maybe_keys) = verifier.verify( + import_block, ).await.map_err(|msg| { if let Some(ref peer) = peer { trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg); @@ -268,16 +277,7 @@ pub(crate) async fn import_single_block_metered, Trans if let Some(keys) = maybe_keys { cache.extend(keys.into_iter()); } - import_block.import_existing = block.import_existing; - let mut import_block = import_block.clear_storage_changes_and_mutate(); - if let Some(state) = block.state { - import_block.state_action = StateAction::ApplyChanges(crate::StorageChanges::Import(state)); - } else if block.skip_execution { - import_block.state_action = StateAction::Skip; - } else if block.allow_missing_state { - import_block.state_action = StateAction::ExecuteIfPossible; - } - + let import_block = import_block.clear_storage_changes_and_mutate(); let imported = import_handle.import_block(import_block, cache).await; if let Some(metrics) = metrics.as_ref() { metrics.report_verification_and_import(started.elapsed()); diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index 5767b72dd8084..8d1c16145c9b1 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -453,12 +453,9 @@ mod tests { impl Verifier for () { async fn verify( &mut self, - origin: BlockOrigin, - header: Header, - _justifications: Option, - _body: Option>, + block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - Ok((BlockImportParams::new(origin, header), None)) + Ok((BlockImportParams::new(block.origin, block.header), None)) } } diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index 5b393bd1d80e6..99efcbd97a116 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -496,7 +496,7 @@ sp_api::decl_runtime_apis! { /// applied in the runtime after those N blocks have passed. /// /// The consensus protocol will coordinate the handoff externally. - #[api_version(2)] + #[api_version(3)] pub trait GrandpaApi { /// Get the current GRANDPA authorities and weights. This should not change except /// for when changes are scheduled and the corresponding delay has passed. @@ -534,5 +534,8 @@ sp_api::decl_runtime_apis! { set_id: SetId, authority_id: AuthorityId, ) -> Option; + + /// Get current GRANDPA authority set id. + fn current_set_id() -> SetId; } } diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 7ee1072a7b83e..183ae5c4acf20 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -838,6 +838,10 @@ cfg_if! { Vec::new() } + fn current_set_id() -> sp_finality_grandpa::SetId { + 0 + } + fn submit_report_equivocation_unsigned_extrinsic( _equivocation_proof: sp_finality_grandpa::EquivocationProof< ::Hash, From 32983ea23dcc05cd4353c9001ceb49d2991a9953 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 28 Jun 2021 12:06:25 +0200 Subject: [PATCH 03/20] Warp sync protocol --- Cargo.lock | 56 ++--- Cargo.toml | 1 - bin/node-template/node/src/service.rs | 11 + bin/node/cli/Cargo.toml | 2 - bin/node/cli/src/service.rs | 17 +- client/consensus/epochs/src/lib.rs | 2 +- client/finality-grandpa-warp-sync/Cargo.toml | 36 ---- client/finality-grandpa/Cargo.toml | 2 +- client/finality-grandpa/src/environment.rs | 2 +- client/finality-grandpa/src/lib.rs | 1 + .../src/proof.rs | 199 +++++++++++++++--- client/network/Cargo.toml | 1 + client/network/src/behaviour.rs | 32 ++- client/network/src/config.rs | 4 + client/network/src/gossip/tests.rs | 1 + client/network/src/lib.rs | 1 + client/network/src/protocol.rs | 64 +++++- client/network/src/protocol/sync.rs | 114 ++++++++-- client/network/src/protocol/sync/warp.rs | 142 ++++++++++--- client/network/src/service.rs | 8 + client/network/src/service/tests.rs | 1 + .../src/warp_request_handler.rs} | 125 +++++------ client/network/test/src/lib.rs | 2 + client/service/src/builder.rs | 22 +- test-utils/test-runner/src/node.rs | 1 + 25 files changed, 607 insertions(+), 240 deletions(-) delete mode 100644 client/finality-grandpa-warp-sync/Cargo.toml rename client/{finality-grandpa-warp-sync => finality-grandpa}/src/proof.rs (64%) rename client/{finality-grandpa-warp-sync/src/lib.rs => network/src/warp_request_handler.rs} (54%) diff --git a/Cargo.lock b/Cargo.lock index ee78c31645b43..695e3491dff21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1690,7 +1690,7 @@ dependencies = [ "num-traits", "parity-scale-codec", "parking_lot 0.11.1", - "rand 0.8.3", + "rand 0.8.4", ] [[package]] @@ -1700,7 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand 0.8.3", + "rand 0.8.4", "rustc-hex", "static_assertions", ] @@ -3365,7 +3365,7 @@ dependencies = [ "libp2p-core", "libp2p-swarm", "log", - "rand 0.8.3", + "rand 0.8.4", "smallvec 1.6.1", "socket2 0.4.0", "void", @@ -4208,7 +4208,6 @@ dependencies = [ "sc-consensus-slots", "sc-consensus-uncles", "sc-finality-grandpa", - "sc-finality-grandpa-warp-sync", "sc-keystore", "sc-network", "sc-offchain", @@ -4885,7 +4884,7 @@ dependencies = [ "paste 1.0.4", "pretty_assertions 0.7.2", "pwasm-utils", - "rand 0.8.3", + "rand 0.8.4", "rand_pcg 0.3.0", "serde", "smallvec 1.6.1", @@ -5736,7 +5735,7 @@ dependencies = [ "log", "memmap2", "parking_lot 0.11.1", - "rand 0.8.3", + "rand 0.8.4", ] [[package]] @@ -6438,7 +6437,7 @@ checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger 0.8.3", "log", - "rand 0.8.3", + "rand 0.8.4", ] [[package]] @@ -6506,9 +6505,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha 0.3.0", @@ -7562,7 +7561,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.11.1", "pin-project 1.0.5", - "rand 0.7.3", + "rand 0.8.4", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -7624,32 +7623,6 @@ dependencies = [ "substrate-test-runtime-client", ] -[[package]] -name = "sc-finality-grandpa-warp-sync" -version = "0.9.0" -dependencies = [ - "derive_more", - "finality-grandpa", - "futures 0.3.15", - "log", - "num-traits", - "parity-scale-codec", - "parking_lot 0.11.1", - "prost", - "rand 0.8.3", - "sc-block-builder", - "sc-client-api", - "sc-finality-grandpa", - "sc-network", - "sc-service", - "sp-blockchain", - "sp-consensus", - "sp-finality-grandpa", - "sp-keyring", - "sp-runtime", - "substrate-test-runtime-client", -] - [[package]] name = "sc-informant" version = "0.9.0" @@ -7749,6 +7722,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", + "sp-finality-grandpa", "sp-keyring", "sp-runtime", "sp-test-primitives", @@ -8661,7 +8635,7 @@ dependencies = [ "futures 0.3.15", "httparse", "log", - "rand 0.8.3", + "rand 0.8.4", "sha-1 0.9.4", ] @@ -9929,7 +9903,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", - "rand 0.8.3", + "rand 0.8.4", "redox_syscall 0.2.5", "remove_dir_all", "winapi 0.3.9", @@ -10599,7 +10573,7 @@ dependencies = [ "ipnet", "lazy_static", "log", - "rand 0.8.3", + "rand 0.8.4", "smallvec 1.6.1", "thiserror", "tinyvec", @@ -11265,7 +11239,7 @@ dependencies = [ "mach", "memoffset 0.6.1", "more-asserts", - "rand 0.8.3", + "rand 0.8.4", "region", "thiserror", "wasmtime-environ", @@ -11434,7 +11408,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot 0.11.1", - "rand 0.8.3", + "rand 0.8.4", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index f7552f0bbbc48..abb2599c458a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ members = [ "client/executor/wasmi", "client/executor/wasmtime", "client/finality-grandpa", - "client/finality-grandpa-warp-sync", "client/informant", "client/keystore", "client/light", diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index c19824e9eaa38..42b628e0ef8d0 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -148,6 +148,10 @@ pub fn new_full(mut config: Configuration) -> Result } config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); + let warp_sync = Arc::new(sc_finality_grandpa::proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + )); let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -158,6 +162,7 @@ pub fn new_full(mut config: Configuration) -> Result import_queue, on_demand: None, block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; if config.offchain_worker.enabled { @@ -371,6 +376,11 @@ pub fn new_light(mut config: Configuration) -> Result }, )?; + let warp_sync = Arc::new(sc_finality_grandpa::proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + )); + let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -380,6 +390,7 @@ pub fn new_light(mut config: Configuration) -> Result import_queue, on_demand: Some(on_demand.clone()), block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; if config.offchain_worker.enabled { diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 9fcd0875e8dca..650adc690f831 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -76,7 +76,6 @@ sc-service = { version = "0.9.0", default-features = false, path = "../../../cli sc-tracing = { version = "3.0.0", path = "../../../client/tracing" } sc-telemetry = { version = "3.0.0", path = "../../../client/telemetry" } sc-authority-discovery = { version = "0.9.0", path = "../../../client/authority-discovery" } -sc-finality-grandpa-warp-sync = { version = "0.9.0", path = "../../../client/finality-grandpa-warp-sync", optional = true } # frame dependencies pallet-indices = { version = "3.0.0", path = "../../../frame/indices" } @@ -160,7 +159,6 @@ cli = [ "frame-benchmarking-cli", "substrate-frame-cli", "sc-service/db", - "sc-finality-grandpa-warp-sync", "structopt", "substrate-build-script-utils", "try-runtime-cli", diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 8fa3d2ed77ceb..b267d81cdab65 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -230,16 +230,10 @@ pub fn new_full_base( let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; 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(), + let warp_sync = Arc::new(grandpa::proof::NetworkProvider::new( backend.clone(), import_setup.1.shared_authority_set().clone(), - ) - ); + )); let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -250,6 +244,7 @@ pub fn new_full_base( import_queue, on_demand: None, block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; if config.offchain_worker.enabled { @@ -518,6 +513,11 @@ pub fn new_light_base( telemetry.as_ref().map(|x| x.handle()), )?; + let warp_sync = Arc::new(grandpa::proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + )); + let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -527,6 +527,7 @@ pub fn new_light_base( import_queue, on_demand: Some(on_demand.clone()), block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; let enable_grandpa = !config.disable_grandpa; diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 75fe54d84fc81..0e5fca0b0cc62 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -250,7 +250,7 @@ impl<'a, E: Epoch> From<&'a PersistedEpoch> for PersistedEpochHeader { } /// Persisted epoch header stored in ForkTree. -#[derive(Encode, Decode, PartialEq, Eq, Debug)] +#[derive(Encode, Decode, PartialEq, Eq)] pub enum PersistedEpochHeader { /// Genesis persisted epoch header. epoch_0, epoch_1. Genesis(EpochHeader, EpochHeader), diff --git a/client/finality-grandpa-warp-sync/Cargo.toml b/client/finality-grandpa-warp-sync/Cargo.toml deleted file mode 100644 index 27728e159c762..0000000000000 --- a/client/finality-grandpa-warp-sync/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -description = "A request-response protocol for handling grandpa warp sync requests" -name = "sc-finality-grandpa-warp-sync" -version = "0.9.0" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -authors = ["Parity Technologies "] -edition = "2018" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/substrate/" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0" } -derive_more = "0.99.11" -futures = "0.3.8" -log = "0.4.11" -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.1" } -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" } diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 3cb577aee5db8..436fb6ba00554 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -22,7 +22,7 @@ futures = "0.3.9" futures-timer = "3.0.1" log = "0.4.8" parking_lot = "0.11.1" -rand = "0.7.2" +rand = "0.8.4" parity-scale-codec = { version = "2.0.0", features = ["derive"] } sp-application-crypto = { version = "3.0.0", path = "../../primitives/application-crypto" } sp-arithmetic = { version = "3.0.0", path = "../../primitives/arithmetic" } diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index 964e199f90968..37a4aadc817bb 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -1095,7 +1095,7 @@ where // random between `[0, 2 * gossip_duration]` seconds. let delay: u64 = - thread_rng().gen_range(0, 2 * self.config.gossip_duration.as_millis() as u64); + thread_rng().gen_range(0 .. 2 * self.config.gossip_duration.as_millis() as u64); Box::pin(Delay::new(Duration::from_millis(delay)).map(Ok)) } diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 9597368149684..5c2a704afc796 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -120,6 +120,7 @@ mod notification; mod observer; mod until_imported; mod voting_rule; +pub mod proof; pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet}; pub use aux_schema::best_justification; diff --git a/client/finality-grandpa-warp-sync/src/proof.rs b/client/finality-grandpa/src/proof.rs similarity index 64% rename from client/finality-grandpa-warp-sync/src/proof.rs rename to client/finality-grandpa/src/proof.rs index 87a6220267827..59b80c3a951a3 100644 --- a/client/finality-grandpa-warp-sync/src/proof.rs +++ b/client/finality-grandpa/src/proof.rs @@ -14,11 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use codec::{Decode, Encode}; + +//! Utilities for generating and verifying GRANDPA warp sync proofs. + +use sp_runtime::codec::{Decode, Encode, self}; use sc_client_api::Backend as ClientBackend; -use sc_finality_grandpa::{ +use crate::{ find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification, + VoterSet, best_justification, SharedAuthoritySet, }; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID}; @@ -26,11 +30,33 @@ use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor, One}, }; +use sc_network::warp_request_handler::{ + WarpSyncProvider, EncodedProof, EncodedJustification, VerificationResult +}; -use crate::HandleRequestError; +use std::sync::Arc; + +/// Warp proof processing error. +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum Error { + /// Decoding error. + #[display(fmt = "Failed to decode block hash: {}.", _0)] + DecodeScale(codec::Error), + /// Client backend error. + Client(sp_blockchain::Error), + /// Invalid request data. + #[from(ignore)] + InvalidRequest(String), + /// Invalid warp proof. + #[from(ignore)] + InvalidProof(String), + /// Missing header or authority set change data. + #[display(fmt = "Missing required data to be able to answer request.")] + MissingData, +} /// The maximum size in bytes of the `WarpSyncProof`. -pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 16 * 1024 * 1024; +pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 8 * 1024 * 1024; /// A proof of an authority set change. #[derive(Decode, Encode)] @@ -54,11 +80,11 @@ impl WarpSyncProof { /// Generates a warp sync proof starting at the given block. It will generate authority set /// change proofs for all changes that happened from `begin` until the current authority set /// (capped by MAX_WARP_SYNC_PROOF_SIZE). - pub fn generate( + fn generate( backend: &Backend, begin: Block::Hash, set_changes: &AuthoritySetChanges>, - ) -> Result, HandleRequestError> + ) -> Result, Error> where Backend: ClientBackend, { @@ -67,10 +93,10 @@ impl WarpSyncProof { let begin_number = blockchain .block_number_from_id(&BlockId::Hash(begin))? - .ok_or_else(|| HandleRequestError::InvalidRequest("Missing start block".to_string()))?; + .ok_or_else(|| Error::InvalidRequest("Missing start block".to_string()))?; if begin_number > blockchain.info().finalized_number { - return Err(HandleRequestError::InvalidRequest( + return Err(Error::InvalidRequest( "Start block is not finalized".to_string(), )); } @@ -82,7 +108,7 @@ impl WarpSyncProof { ); if canon_hash != begin { - return Err(HandleRequestError::InvalidRequest( + return Err(Error::InvalidRequest( "Start block is not in the finalized chain".to_string(), )); } @@ -92,7 +118,7 @@ impl WarpSyncProof { let mut proof_limit_reached = false; let set_changes = set_changes.iter_from(begin_number) - .ok_or(HandleRequestError::MissingData)?; + .ok_or(Error::MissingData)?; for (_, last_block) in set_changes { let header = blockchain.header(BlockId::Number(*last_block))?.expect( @@ -141,7 +167,7 @@ impl WarpSyncProof { false } else { let latest_justification = - sc_finality_grandpa::best_justification(backend)?.filter(|justification| { + best_justification(backend)?.filter(|justification| { // the existing best justification must be for a block higher than the // last authority set change. if we didn't prove any authority set // change then we fallback to make sure it's higher or equal to the @@ -176,53 +202,156 @@ impl WarpSyncProof { } /// Verifies the warp sync proof starting at the given set id and with the given authorities. + /// Verification stops when either the proof is exhausted or finality for the target header can be proven. /// If the proof is valid the new set id and authorities is returned. - pub fn verify( + fn verify( &self, set_id: SetId, authorities: AuthorityList, - ) -> Result<(SetId, AuthorityList), HandleRequestError> + target_header: &Block::Header, + target_justification: &[u8], + ) -> Result, String> where NumberFor: BlockNumberOps, { let mut current_set_id = set_id; let mut current_authorities = authorities; + let mut current_hash = Default::default(); + let target_hash = target_header.hash(); for (fragment_num, proof) in self.proofs.iter().enumerate() { + if proof.justification.target().0 > *target_header.number() { + Self::verify_justification( + target_justification, + (target_hash.clone(), *target_header.number()), + current_set_id, + ¤t_authorities, + ).map_err(|e| format!("Target block verification error: {:?}", e))?; + return Ok(VerificationResult::::Complete(current_set_id, current_authorities)); + } + proof .justification .verify(current_set_id, ¤t_authorities) - .map_err(|err| HandleRequestError::InvalidProof(err.to_string()))?; + .map_err(|err| err.to_string())?; - if proof.justification.target().1 != proof.header.hash() { - return Err(HandleRequestError::InvalidProof( - "mismatch between header and justification".to_owned() - )); + current_hash = proof.header.hash(); + if proof.justification.target().1 != current_hash { + return Err("Mismatch between header and justification".to_owned()); } if let Some(scheduled_change) = find_scheduled_change::(&proof.header) { current_authorities = scheduled_change.next_authorities; current_set_id += 1; - } else if fragment_num != self.proofs.len() - 1 { - // Only the last fragment of the proof is allowed to be missing the authority + } else if fragment_num != self.proofs.len() - 1 || !self.is_finished { + // Only the last fragment of the last proof is allowed to be missing the authority // set change. - return Err(HandleRequestError::InvalidProof( - "Header is missing authority set change digest".to_string(), - )); + return Err("Header is missing authority set change digest".to_string()); + } + + if current_hash == target_hash { + return Ok(VerificationResult::::Complete(current_set_id, current_authorities)) } } - Ok((current_set_id, current_authorities)) + if self.is_finished { + Self::verify_justification( + target_justification, + (target_hash, *target_header.number()), + current_set_id, + ¤t_authorities, + ).map_err(|e| format!("Target block verification error: {:?}", e))?; + Ok(VerificationResult::::Complete(current_set_id, current_authorities)) + } else { + Ok(VerificationResult::::Partial(current_set_id, current_authorities, current_hash)) + } + } + + /// Verifies the block finality justification against given authority set. + fn verify_justification( + encoded_justification: &[u8], + finalized_target: (Block::Hash, NumberFor), + set_id: u64, + authorities: &AuthorityList, + ) -> Result<(), Error> + where + NumberFor: BlockNumberOps, + { + let voters = VoterSet::new(authorities.iter().cloned()) + .ok_or_else(|| Error::InvalidProof("Can't create voter set".to_string()))?; + + GrandpaJustification::::decode_and_verify_finalizes( + encoded_justification, + finalized_target, + set_id, + &voters + ).map_err(|err| Error::InvalidProof(err.to_string()))?; + Ok(()) + } +} + +/// Implements network API for warp sync. +pub struct NetworkProvider> +where + NumberFor: BlockNumberOps, +{ + backend: Arc, + authority_set: SharedAuthoritySet>, +} + +impl> NetworkProvider +where + NumberFor: BlockNumberOps, +{ + /// Create a new istance for a given backend and authority set. + pub fn new( + backend: Arc, + authority_set: SharedAuthoritySet> + ) -> Self { + NetworkProvider { + backend, + authority_set, + } + } +} + +impl > WarpSyncProvider + for NetworkProvider +where + NumberFor: BlockNumberOps, +{ + fn generate(&self, start: Block::Hash) -> Result { + let proof = WarpSyncProof::::generate( + &*self.backend, + start, + &self.authority_set.authority_set_changes(), + ).map_err(|e| e.to_string())?; + Ok(EncodedProof(proof.encode())) + } + + fn verify(&self, + proof: &EncodedProof, + set_id: SetId, + authorities: AuthorityList, + target_header: &Block::Header, + target_justification: &EncodedJustification, + ) -> Result, String> + { + let EncodedProof(proof) = proof; + let EncodedJustification(target_justification) = target_justification; + let proof = WarpSyncProof::::decode(&mut proof.as_slice()) + .map_err(|e| format!("Proof decoding error: {:?}", e))?; + proof.verify(set_id, authorities, target_header, target_justification.as_slice()) } } #[cfg(test)] mod tests { - use crate::WarpSyncProof; - use codec::Encode; + use super::WarpSyncProof; + use super::codec::Encode; + use crate::{AuthoritySetChanges, GrandpaJustification}; use rand::prelude::*; use sc_block_builder::BlockBuilderProvider; - use sc_finality_grandpa::{AuthoritySetChanges, GrandpaJustification}; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use sp_finality_grandpa::GRANDPA_ENGINE_ID; @@ -233,6 +362,7 @@ mod tests { ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, }; + use sc_network::warp_request_handler::VerificationResult; #[test] fn warp_sync_proof_generate_verify() { @@ -247,6 +377,7 @@ mod tests { let mut current_authorities = vec![Ed25519Keyring::Alice]; let mut current_set_id = 0; let mut authority_set_changes = Vec::new(); + let mut last_block = None; for n in 1..=100 { let mut block = client @@ -288,6 +419,7 @@ mod tests { block.header.digest_mut().logs.push(digest); } + let header = block.header.clone(); futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); if let Some(new_authorities) = new_authorities { @@ -337,6 +469,7 @@ mod tests { current_set_id += 1; current_authorities = new_authorities; + last_block = Some((header, justification)); } } @@ -348,8 +481,18 @@ mod tests { let warp_sync_proof = WarpSyncProof::generate(&*backend, genesis_hash, &authority_set_changes).unwrap(); + let (last_header, last_justification) = last_block.unwrap(); + // verifying the proof should yield the last set id and authorities - let (new_set_id, new_authorities) = warp_sync_proof.verify(0, genesis_authorities).unwrap(); + let (new_set_id, new_authorities) = match warp_sync_proof.verify( + 0, + genesis_authorities, + &last_header, + &last_justification.encode(), + ).unwrap() { + VerificationResult::Complete(new_set_id, new_authorities) => (new_set_id, new_authorities), + _ => panic!("Unexpected result"), + }; let expected_authorities = current_authorities .iter() diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 3740ebceb6389..933470a988f79 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -56,6 +56,7 @@ sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" } sp-core = { version = "3.0.0", path = "../../primitives/core" } sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" } sp-utils = { version = "3.0.0", path = "../../primitives/utils" } +sp-finality-grandpa = { version = "3.0.0", path = "../../primitives/finality-grandpa" } thiserror = "1" unsigned-varint = { version = "0.6.0", features = ["futures", "asynchronous_codec"] } void = "1.0.2" diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 576c49d1da366..371f0338a5f24 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -36,6 +36,7 @@ use libp2p::swarm::{ }; use log::debug; use prost::Message; +use codec::Encode; use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}}; use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justifications}; use std::{ @@ -84,6 +85,11 @@ pub struct Behaviour { /// [`request_responses::RequestResponsesBehaviour`]. #[behaviour(ignore)] state_request_protocol_name: String, + + /// Protocol name used to send out warp sync requests via + /// [`request_responses::RequestResponsesBehaviour`]. + #[behaviour(ignore)] + warp_sync_protocol_name: Option, } /// Event generated by `Behaviour`. @@ -192,6 +198,7 @@ impl Behaviour { disco_config: DiscoveryConfig, block_request_protocol_config: request_responses::ProtocolConfig, state_request_protocol_config: request_responses::ProtocolConfig, + warp_sync_protocol_config: Option, bitswap: Option>, light_client_request_protocol_config: request_responses::ProtocolConfig, // All remaining request protocol configs. @@ -200,9 +207,16 @@ impl Behaviour { // Extract protocol name and add to `request_response_protocols`. let block_request_protocol_name = block_request_protocol_config.name.to_string(); let state_request_protocol_name = state_request_protocol_config.name.to_string(); + let warp_sync_protocol_name = match warp_sync_protocol_config { + Some(config) => { + let name = config.name.to_string(); + request_response_protocols.push(config); + Some(name) + }, + None => None, + }; request_response_protocols.push(block_request_protocol_config); request_response_protocols.push(state_request_protocol_config); - request_response_protocols.push(light_client_request_protocol_config); Ok(Behaviour { @@ -216,6 +230,7 @@ impl Behaviour { events: VecDeque::new(), block_request_protocol_name, state_request_protocol_name, + warp_sync_protocol_name, }) } @@ -352,6 +367,21 @@ Behaviour { &target, &self.state_request_protocol_name, buf, pending_response, IfDisconnected::ImmediateError, ); }, + CustomMessageOutcome::WarpSyncRequest { target, request, pending_response } => { + match &self.warp_sync_protocol_name { + Some(name) => self.request_responses.send_request( + &target, name, request.encode(), pending_response, IfDisconnected::ImmediateError, + ), + None => { + log::warn!( + target: "sync", + "Trying to send warp sync request when no protocol is configured {:?}", + request, + ); + return + } + } + }, CustomMessageOutcome::NotificationStreamOpened { remote, protocol, negotiated_fallback, roles, notifications_sink } => { diff --git a/client/network/src/config.rs b/client/network/src/config.rs index f141b568355e4..cc981f64c9ed4 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -23,6 +23,7 @@ pub use crate::chain::Client; pub use crate::on_demand_layer::{AlwaysBadChecker, OnDemand}; +pub use crate::warp_request_handler::WarpSyncProvider; pub use crate::request_responses::{ IncomingRequest, OutgoingResponse, @@ -132,6 +133,9 @@ pub struct Params { /// [`crate::state_requests::handler::StateRequestHandler::new`] allowing /// both outgoing and incoming requests. pub state_request_protocol_config: RequestResponseConfig, + + /// Optional warp sync protocol support. Include protocol config and sync provider. + pub warp_sync: Option<(Arc>, RequestResponseConfig)>, } /// Role of the local node. diff --git a/client/network/src/gossip/tests.rs b/client/network/src/gossip/tests.rs index 8e8d46da4cc8a..a4fc9832f5371 100644 --- a/client/network/src/gossip/tests.rs +++ b/client/network/src/gossip/tests.rs @@ -138,6 +138,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration) block_request_protocol_config, state_request_protocol_config, light_client_request_protocol_config, + warp_sync: None, }) .unwrap(); diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 11e235bb81ae7..0d544d30f1f55 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -261,6 +261,7 @@ pub mod block_request_handler; pub mod bitswap; pub mod light_client_requests; pub mod state_request_handler; +pub mod warp_request_handler; pub mod config; pub mod error; pub mod gossip; diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 8c68376c04b69..f27942025c6f5 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -18,11 +18,12 @@ use crate::{ chain::Client, - config::{self, ProtocolId}, + config::{self, ProtocolId, WarpSyncProvider}, error, request_responses::RequestFailure, utils::{interval, LruHashSet}, schema::v1::StateResponse, + warp_request_handler::EncodedProof, }; use bytes::Bytes; @@ -184,6 +185,7 @@ pub struct Protocol { enum PeerRequest { Block(message::BlockRequest), State, + WarpProof, } /// Peer information @@ -284,6 +286,7 @@ impl Protocol { notifications_protocols_handshakes: Vec>, block_announce_validator: Box + Send>, metrics_registry: Option<&Registry>, + warp_sync_provider: Option>>, ) -> error::Result<(Protocol, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> { let info = chain.info(); let sync = ChainSync::new( @@ -291,6 +294,7 @@ impl Protocol { chain.clone(), block_announce_validator, config.max_parallel_downloads, + warp_sync_provider, ).map_err(Box::new)?; let boot_node_ids = { @@ -690,9 +694,28 @@ impl Protocol { match self.sync.on_state_data(&peer_id, response) { Ok(sync::OnStateData::Import(origin, block)) => CustomMessageOutcome::BlockImport(origin, vec![block]), - Ok(sync::OnStateData::Request(peer, req)) => { - prepare_state_request::(&mut self.peers, peer, req) + Ok(sync::OnStateData::Request(peer, req)) => + prepare_state_request::(&mut self.peers, peer, req), + Err(sync::BadPeer(id, repu)) => { + self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); + self.peerset_handle.report_peer(id, repu); + CustomMessageOutcome::None } + } + } + + /// Must be called in response to a [`CustomMessageOutcome::WarpSyncRequest`] being emitted. + /// Must contain the same `PeerId` and request that have been emitted. + pub fn on_warp_sync_response( + &mut self, + peer_id: PeerId, + response: crate::warp_request_handler::EncodedProof, + ) -> CustomMessageOutcome { + match self.sync.on_warp_sync_data(&peer_id, response) { + Ok(sync::OnWarpSyncData::WarpProofRequest(peer, req)) => + prepare_warp_sync_request::(&mut self.peers, peer, req), + Ok(sync::OnWarpSyncData::StateRequest(peer, req)) => + prepare_state_request::(&mut self.peers, peer, req), Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); @@ -1224,6 +1247,23 @@ fn prepare_state_request( } } +fn prepare_warp_sync_request( + peers: &mut HashMap>, + who: PeerId, + request: crate::warp_request_handler::Request, +) -> CustomMessageOutcome { + let (tx, rx) = oneshot::channel(); + + if let Some(ref mut peer) = peers.get_mut(&who) { + peer.request = Some((PeerRequest::WarpProof, rx)); + } + CustomMessageOutcome::WarpSyncRequest { + target: who, + request: request, + pending_response: tx, + } +} + /// Outcome of an incoming custom message. #[derive(Debug)] #[must_use] @@ -1261,6 +1301,12 @@ pub enum CustomMessageOutcome { request: crate::schema::v1::StateRequest, pending_response: oneshot::Sender, RequestFailure>>, }, + /// A new warp sync request must be emitted. + WarpSyncRequest { + target: PeerId, + request: crate::warp_request_handler::Request, + pending_response: oneshot::Sender, RequestFailure>>, + }, /// Peer has a reported a new head of chain. PeerNewBest(PeerId, NumberFor), /// Now connected to a new peer for syncing purposes. @@ -1324,6 +1370,7 @@ impl NetworkBehaviour for Protocol { // Check for finished outgoing requests. let mut finished_block_requests = Vec::new(); let mut finished_state_requests = Vec::new(); + let mut finished_warp_sync_requests = Vec::new(); for (id, peer) in self.peers.iter_mut() { if let Peer { request: Some((_, pending_response)), .. } = peer { match pending_response.poll_unpin(cx) { @@ -1366,6 +1413,9 @@ impl NetworkBehaviour for Protocol { finished_state_requests.push((id.clone(), protobuf_response)); }, + PeerRequest::WarpProof => { + finished_warp_sync_requests.push((id.clone(), resp)); + }, } }, Poll::Ready(Ok(Err(e))) => { @@ -1425,6 +1475,10 @@ impl NetworkBehaviour for Protocol { let ev = self.on_state_response(id, protobuf_response); self.pending_messages.push_back(ev); } + for (id, response) in finished_warp_sync_requests { + let ev = self.on_warp_sync_response(id, EncodedProof(response)); + self.pending_messages.push_back(ev); + } while let Poll::Ready(Some(())) = self.tick_timeout.poll_next_unpin(cx) { self.tick(); @@ -1442,6 +1496,10 @@ impl NetworkBehaviour for Protocol { let event = prepare_block_request(&mut self.peers, id, request); self.pending_messages.push_back(event); } + if let Some((id, request)) = self.sync.warp_sync_request() { + let event = prepare_warp_sync_request(&mut self.peers, id, request); + self.pending_messages.push_back(event); + } // Check if there is any block announcement validation finished. while let Poll::Ready(result) = self.sync.poll_block_announce_validation(cx) { diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 2ad120fc07c94..3f7dd732111b3 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -32,7 +32,7 @@ use codec::Encode; use blocks::BlockCollection; use state::StateSync; -use warp::WarpSync; +use warp::{WarpSync, WarpSyncProvider, WarpProofRequest}; use sp_blockchain::{Error as ClientError, HeaderMetadata}; use sp_consensus::{BlockOrigin, BlockStatus, block_validation::{BlockAnnounceValidator, Validation}, @@ -218,6 +218,8 @@ pub struct ChainSync { state_sync: Option>, /// Warp sync in progress, if any. warp_sync: Option>, + /// Warp sync provider. + warp_sync_provider: Option>>, /// Enable importing existing blocks. This is used used after the state download to /// catch up to the latest state while re-importing blocks. import_existing: bool, @@ -296,7 +298,9 @@ pub enum PeerSyncState { /// Downloading state. DownloadingState, /// Downloading warp block. - DownloadingWarp, + DownloadingWarpBlock, + /// Downloading warp proof. + DownloadingWarpProof, } impl PeerSyncState { @@ -384,6 +388,15 @@ pub enum OnStateData { Request(PeerId, StateRequest) } +/// Result of [`ChainSync::on_warp_sync_data`]. +#[derive(Debug)] +pub enum OnWarpSyncData { + /// The block and state that should be imported. + WarpProofRequest(PeerId, warp::WarpProofRequest), + /// A new state request needs to be made to the given peer. + StateRequest(PeerId, StateRequest) +} + /// Result of [`ChainSync::poll_block_announce_validation`]. #[derive(Debug, Clone, PartialEq, Eq)] pub enum PollBlockAnnounceValidation { @@ -502,6 +515,7 @@ impl ChainSync { client: Arc>, block_announce_validator: Box + Send>, max_parallel_downloads: u32, + warp_sync_provider: Option>>, ) -> Result { let mut sync = ChainSync { client, @@ -521,6 +535,7 @@ impl ChainSync { block_announce_validation_per_peer_stats: Default::default(), state_sync: None, warp_sync: None, + warp_sync_provider, import_existing: false, }; sync.reset_sync_start_point()?; @@ -639,7 +654,9 @@ impl ChainSync { "Starting warp state sync for #{}", number, ); - self.warp_sync = Some(WarpSync::new(self.client.clone(), number)); + if let Some(provider) = &self.warp_sync_provider { + self.warp_sync = Some(WarpSync::new(self.client.clone(), provider.clone(), number)); + } } } // If we are at genesis, just start downloading. @@ -804,7 +821,7 @@ impl ChainSync { /// Get an iterator over all block requests of all peers. pub fn block_requests(&mut self) -> impl Iterator)> + '_ { if let Some(warp_sync) = &self.warp_sync { - if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarp) { + if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpBlock) { // Only one pending warp request is allowed. return Either::Left(Either::Left(std::iter::empty())) } @@ -816,7 +833,7 @@ impl ChainSync { } if peer.best_number >= warp_sync.target_block_number() { - peer.state = PeerSyncState::DownloadingWarp; + peer.state = PeerSyncState::DownloadingWarpBlock; Some((id, peer.best_number)) } else { None @@ -922,7 +939,7 @@ impl ChainSync { Either::Right(iter) } - /// Get a state request, if any + /// Get a state request, if any. pub fn state_request(&mut self) -> Option<(PeerId, StateRequest)> { if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { // Only one pending state request is allowed. @@ -942,10 +959,10 @@ impl ChainSync { } } if let Some(sync) = &self.warp_sync { + if sync.is_complete() { + return None; + } if let Some(request) = sync.next_state_request() { - if sync.is_complete() { - return None; - } for (id, peer) in self.peers.iter_mut() { if peer.state.is_available() && peer.best_number >= sync.target_block_number() { trace!(target: "sync", "New StateRequest for {}", id); @@ -958,6 +975,29 @@ impl ChainSync { None } + /// Get a warp sync request, if any. + pub fn warp_sync_request(&mut self) -> Option<(PeerId, WarpProofRequest)> { + if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpProof) { + // Only one pending state request is allowed. + return None; + } + if let Some(sync) = &self.warp_sync { + if sync.is_complete() { + return None; + } + if let Some(request) = sync.next_warp_poof_request() { + for (id, peer) in self.peers.iter_mut() { + if peer.state.is_available() && peer.best_number >= sync.target_block_number() { + trace!(target: "sync", "New WarpProofRequest for {}", id); + peer.state = PeerSyncState::DownloadingWarpProof; + return Some((id.clone(), request)) + } + } + } + } + None + } + /// Handle a response from the remote to a block request that we made. /// /// `request` must be the original request that triggered `response`. @@ -977,12 +1017,11 @@ impl ChainSync { } let result = sync.import_block(response); return match result { - warp::ImportResult::Continue(_) => { + warp::WarpBlockImportResult::WarpProofRequest(_) => { trace!(target: "sync", "Successfully imported warp header from {}", who); Ok(OnBlockData::Ignore) }, - warp::ImportResult::BadResponse => Err(BadPeer(who.clone(), rep::NO_BLOCK)), - warp::ImportResult::Import(..) => Ok(OnBlockData::Ignore), + warp::WarpBlockImportResult::BadResponse => Err(BadPeer(who.clone(), rep::NO_BLOCK)), } } self.downloaded_blocks += response.blocks.len(); @@ -1139,7 +1178,8 @@ impl ChainSync { PeerSyncState::Available | PeerSyncState::DownloadingJustification(..) | PeerSyncState::DownloadingState - | PeerSyncState::DownloadingWarp + | PeerSyncState::DownloadingWarpProof + | PeerSyncState::DownloadingWarpBlock => Vec::new() } } else { @@ -1203,12 +1243,7 @@ impl ChainSync { match import_result { state::ImportResult::Import(hash, header, state) => { - let origin = if self.status().state != SyncState::Downloading { - BlockOrigin::NetworkBroadcast - } else { - BlockOrigin::NetworkInitialSync - }; - + let origin = BlockOrigin::NetworkInitialSync; let block = IncomingBlock { hash, header: Some(header), @@ -1233,6 +1268,41 @@ impl ChainSync { } } + /// Handle a response from the remote to a warp proof request that we made. + /// + /// Returns next request if any. + pub fn on_warp_sync_data( + &mut self, + who: &PeerId, + response: warp::EncodedProof, + ) -> Result, BadPeer> { + let import_result = if let Some(sync) = &mut self.warp_sync { + debug!( + target: "sync", + "Importing warp proof data from {}, {} bytes.", + who, + response.0.len(), + ); + sync.import_warp_proof(response) + } else { + debug!(target: "sync", "Ignored obsolete warp sync response from {}", who); + return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)); + }; + + match import_result { + warp::WarpProofImportResult::StateRequest(request) => { + Ok(OnWarpSyncData::StateRequest(who.clone(), request)) + } + warp::WarpProofImportResult::WarpProofRequest(request) => { + Ok(OnWarpSyncData::WarpProofRequest(who.clone(), request)) + } + warp::WarpProofImportResult::BadResponse => { + debug!(target: "sync", "Bad proof data received from {}", who); + Err(BadPeer(who.clone(), rep::BAD_BLOCK)) + } + } + } + fn validate_and_queue_blocks( &mut self, mut new_blocks: Vec>, @@ -2329,6 +2399,7 @@ mod test { client.clone(), block_announce_validator, 1, + None, ).unwrap(); let (a1_hash, a1_number) = { @@ -2397,6 +2468,7 @@ mod test { client.clone(), Box::new(DefaultBlockAnnounceValidator), 1, + None, ).unwrap(); let peer_id1 = PeerId::random(); @@ -2570,6 +2642,7 @@ mod test { client.clone(), Box::new(DefaultBlockAnnounceValidator), 5, + None, ).unwrap(); let peer_id1 = PeerId::random(); @@ -2683,6 +2756,7 @@ mod test { client.clone(), Box::new(DefaultBlockAnnounceValidator), 5, + None, ).unwrap(); let peer_id1 = PeerId::random(); @@ -2804,6 +2878,7 @@ mod test { client.clone(), Box::new(DefaultBlockAnnounceValidator), 5, + None, ).unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); @@ -2915,6 +2990,7 @@ mod test { client.clone(), Box::new(DefaultBlockAnnounceValidator), 1, + None, ).unwrap(); let peer_id1 = PeerId::random(); diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 3157ac749123c..4af2e0031fe2c 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -17,34 +17,69 @@ // along with this program. If not, see . use std::sync::Arc; -use sp_runtime::traits::{Block as BlockT, Header, NumberFor}; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; +use sp_finality_grandpa::{SetId, AuthorityList}; use crate::schema::v1::{StateRequest, StateResponse}; use crate::chain::Client; use crate::protocol::message; use super::state::StateSync; use crate::StateDownloadProgress; +pub use crate::warp_request_handler::{ + Request as WarpProofRequest, WarpSyncProvider, EncodedProof, VerificationResult, + EncodedJustification, +}; pub use super::state::ImportResult; /// Warp sync support. enum Phase { Block, + WarpProof { + set_id: SetId, + authorities: AuthorityList, + target_justification: EncodedJustification, + target_header: B::Header, + last_hash: B::Hash, + }, State(StateSync), } +/// Import warp proof result. +pub enum WarpProofImportResult { + /// Start downloading state data. + StateRequest(StateRequest), + /// Continue dowloading warp sync proofs. + WarpProofRequest(WarpProofRequest), + /// Bad proof. + BadResponse, +} + +pub enum WarpBlockImportResult { + /// Start downloading state data. + WarpProofRequest(WarpProofRequest), + /// Bad block data. + BadResponse, +} + /// Warp sync state machine. Accumulates target header, warp proofs, state. pub struct WarpSync { target_hash: B::Hash, target_num: NumberFor, phase: Phase, client: Arc>, + warp_sync_provider: Arc>, } impl WarpSync { /// Create a new instance. - pub fn new(client: Arc>, target_num: NumberFor) -> Self { + pub fn new( + client: Arc>, + warp_sync_provider: Arc>, + target_num: NumberFor + ) -> Self { WarpSync { client, + warp_sync_provider, target_hash: Default::default(), target_num, phase: Phase::Block, @@ -54,45 +89,92 @@ impl WarpSync { /// Validate and import a state reponse. pub fn import_state(&mut self, response: StateResponse) -> ImportResult { match &mut self.phase { - Phase::Block => { - log::debug!( - target: "sync", - "Unexpected state response", - ); + Phase::Block | Phase::WarpProof {..} => { + log::debug!(target: "sync", "Unexpected state response"); return ImportResult::BadResponse; } Phase::State(sync) => sync.import(response) } } + /// Validate and import a warp proof reponse. + pub fn import_warp_proof(&mut self, response: EncodedProof) -> WarpProofImportResult { + match &mut self.phase { + Phase::Block | Phase::State(_) => { + log::debug!(target: "sync", "Unexpected warp proof response"); + WarpProofImportResult::BadResponse + }, + Phase::WarpProof { set_id, authorities, target_justification, target_header, last_hash } => { + match self.warp_sync_provider.verify( + &response, + *set_id, + std::mem::take(authorities), + target_header, + target_justification, + ) { + Err(e) => { + log::debug!( target: "sync", "Bad warp proof response: {:?}", e); + return WarpProofImportResult::BadResponse; + }, + Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => { + *set_id = new_set_id; + *authorities = new_authorities; + *last_hash = new_last_hash.clone(); + WarpProofImportResult::WarpProofRequest(WarpProofRequest { + begin: new_last_hash + }) + }, + Ok(VerificationResult::Complete(_, _)) => { + let state_sync = StateSync::new(self.client.clone(), target_header.clone(), false); + let request = state_sync.next_request(); + self.phase = Phase::State(state_sync); + WarpProofImportResult::StateRequest(request) + } + } + } + } + } + /// Validate and import a block reponse. - pub fn import_block(&mut self, mut response: message::BlockResponse) -> ImportResult { + pub fn import_block(&mut self, mut response: message::BlockResponse) -> WarpBlockImportResult { match &mut self.phase { Phase::Block => { if response.blocks.len() != 1 { log::debug!( target: "sync", "Bad block response"); - return ImportResult::BadResponse; + return WarpBlockImportResult::BadResponse; } - let header = match response.blocks.pop().and_then(|r| r.header) { - Some(header) => header, - None => { - log::debug!( target: "sync", "No header in the response"); - return ImportResult::BadResponse; + let (header, justification) = match response.blocks.pop() + .map(|r| (r.header, r.justification)) + { + Some((Some(header), Some(justification))) => (header, justification), + _ => { + log::debug!( target: "sync", "No header or justification in the response"); + return WarpBlockImportResult::BadResponse; } }; self.target_hash = header.hash(); - let state_sync = StateSync::new(self.client.clone(), header, false); - let request = state_sync.next_request(); - self.phase = Phase::State(state_sync); log::debug!( target: "sync", "Warp header received."); - ImportResult::Continue(request) + + //TODO: error here + let last_hash = self.client.hash(Zero::zero()).unwrap() + .expect("Genesis header always exists"); + self.phase = Phase::WarpProof { + set_id: 0, + authorities: AuthorityList::default(), + target_justification: EncodedJustification(justification), + target_header: header, + last_hash: last_hash.clone(), + }; + + let request = WarpProofRequest { begin: last_hash }; + WarpBlockImportResult::WarpProofRequest(request) } - Phase::State(_) => { + Phase::State(_) | Phase::WarpProof {..} => { log::debug!( target: "sync", "Unexpected block response", ); - ImportResult::BadResponse + WarpBlockImportResult::BadResponse } } } @@ -108,18 +190,30 @@ impl WarpSync { direction: message::Direction::Ascending, max: Some(1) }), - Phase::State(_) => None, + Phase::State(_) | Phase::WarpProof {..} => None, } } /// Produce next state request. pub fn next_state_request(&self) -> Option { match &self.phase { - Phase::Block => None, + Phase::Block | Phase::WarpProof {..} => None, Phase::State(sync) => Some(sync.next_request()) } } + /// Produce next warp proof request. + pub fn next_warp_poof_request(&self) -> Option> { + match &self.phase { + Phase::Block | Phase::State(_) => None, + Phase::WarpProof { last_hash, .. } => { + Some(WarpProofRequest { + begin: last_hash.clone(), + }) + } + } + } + /// Return target block hash. pub fn target_block_hash(&self) -> B::Hash { self.target_hash.clone() @@ -133,7 +227,7 @@ impl WarpSync { /// Check if the state is complete. pub fn is_complete(&self) -> bool { match &self.phase { - Phase::Block => false, + Phase::Block | Phase::WarpProof {..} => false, Phase::State(sync) => sync.is_complete(), } } @@ -141,7 +235,7 @@ impl WarpSync { /// Returns state sync estimated progress (percentage, bytes) pub fn progress(&self) -> StateDownloadProgress { match &self.phase { - Phase::Block => StateDownloadProgress { + Phase::Block | Phase::WarpProof {..} => StateDownloadProgress { percentage: 0, size: 0, }, diff --git a/client/network/src/service.rs b/client/network/src/service.rs index fb303312093cd..8e046930ab2f9 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -192,6 +192,12 @@ impl NetworkWorker { ); let default_notif_handshake_message = Roles::from(¶ms.role).encode(); + + let (warp_sync_provider, warp_sync_protocol_config) = match params.warp_sync { + Some((p, c)) => (Some(p), Some(c)), + None => (None, None), + }; + let (protocol, peerset_handle, mut known_addresses) = Protocol::new( protocol::ProtocolConfig { roles: From::from(¶ms.role), @@ -205,6 +211,7 @@ impl NetworkWorker { .map(|_| default_notif_handshake_message.clone())).collect(), params.block_announce_validator, params.metrics_registry.as_ref(), + warp_sync_provider, )?; // List of multiaddresses that we know in the network. @@ -341,6 +348,7 @@ impl NetworkWorker { discovery_config, params.block_request_protocol_config, params.state_request_protocol_config, + warp_sync_protocol_config, bitswap, params.light_client_request_protocol_config, params.network_config.request_response_protocols, diff --git a/client/network/src/service/tests.rs b/client/network/src/service/tests.rs index 77c3d412c401f..761bf522d1620 100644 --- a/client/network/src/service/tests.rs +++ b/client/network/src/service/tests.rs @@ -138,6 +138,7 @@ fn build_test_full_node(config: config::NetworkConfiguration) block_request_protocol_config, state_request_protocol_config, light_client_request_protocol_config, + warp_sync: None, }) .unwrap(); diff --git a/client/finality-grandpa-warp-sync/src/lib.rs b/client/network/src/warp_request_handler.rs similarity index 54% rename from client/finality-grandpa-warp-sync/src/lib.rs rename to client/network/src/warp_request_handler.rs index c0ef93e625fd8..4a4b0da7b6bcb 100644 --- a/client/finality-grandpa-warp-sync/src/lib.rs +++ b/client/network/src/warp_request_handler.rs @@ -16,58 +16,59 @@ //! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer. -use codec::{Decode, Encode}; -use sc_network::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig}; -use sc_client_api::Backend; -use sp_runtime::traits::NumberFor; +use codec::{Encode, Decode}; +use crate::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig}; use futures::channel::{mpsc, oneshot}; use futures::stream::StreamExt; use log::debug; use sp_runtime::traits::Block as BlockT; +use sp_finality_grandpa::{SetId, AuthorityList}; use std::time::Duration; use std::sync::Arc; -use sc_service::{SpawnTaskHandle, config::{Configuration, Role}}; -use sc_finality_grandpa::SharedAuthoritySet; - -mod proof; - -pub use proof::{WarpSyncFragment, WarpSyncProof}; - -/// Generates the appropriate [`RequestResponseConfig`] for a given chain configuration. -pub fn request_response_config_for_chain + 'static>( - config: &Configuration, - spawn_handle: SpawnTaskHandle, - backend: Arc, - authority_set: SharedAuthoritySet>, -) -> RequestResponseConfig -where - NumberFor: 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(), - authority_set, - ); - spawn_handle.spawn("grandpa-warp-sync", handler.run()); - request_response_config - } + +/// Scale-encided warp sync proof response +pub struct EncodedProof(pub Vec); +/// Scale-encided warp sync proof +pub struct EncodedJustification(pub Vec); + +/// Warp sync request +#[derive(Encode, Decode, Debug)] +pub struct Request { + /// Start collecting proofs from this block. + pub begin: B::Hash, } -const LOG_TARGET: &str = "finality-grandpa-warp-sync-request-handler"; +const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024; + +/// Proof verification result. +pub enum VerificationResult { + /// Proof is valid, but the target was not reached. + Partial(SetId, AuthorityList, Block::Hash), + /// Target finality is proved. + Complete(SetId, AuthorityList), +} + +/// Warp sync backend. Handles retrieveing and verifying warp sync proofs. +pub trait WarpSyncProvider: Send + Sync { + /// Generate proof starting at given block hash. The proof is accumulated until maximum proof size is reached. + fn generate(&self, start: B::Hash) -> Result; + /// Verify warp proof agains current set of authorities. + fn verify( + &self, + proof: &EncodedProof, + set_id: SetId, + authorities: AuthorityList, + target_header: &B::Header, + target_justification: &EncodedJustification, + ) -> Result, String>; +} /// 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: proof::MAX_WARP_SYNC_PROOF_SIZE as u64, + max_response_size: MAX_RESPONSE_SIZE, request_timeout: Duration::from_secs(10), inbound_queue: None, } @@ -82,25 +83,17 @@ fn generate_protocol_name(protocol_id: ProtocolId) -> String { s } -#[derive(Decode)] -struct Request { - begin: B::Hash, -} - /// Handler for incoming grandpa warp sync requests from a remote peer. -pub struct GrandpaWarpSyncRequestHandler { - backend: Arc, - authority_set: SharedAuthoritySet>, +pub struct RequestHandler { + backend: Arc>, request_receiver: mpsc::Receiver, - _phantom: std::marker::PhantomData, } -impl> GrandpaWarpSyncRequestHandler { - /// Create a new [`GrandpaWarpSyncRequestHandler`]. +impl RequestHandler { + /// Create a new [`RequestHandler`]. pub fn new( protocol_id: ProtocolId, - backend: Arc, - authority_set: SharedAuthoritySet>, + backend: Arc>, ) -> (Self, RequestResponseConfig) { let (tx, request_receiver) = mpsc::channel(20); @@ -111,8 +104,6 @@ impl> GrandpaWarpSyncRequestHandler> GrandpaWarpSyncRequestHandler, pending_response: oneshot::Sender, - ) -> Result<(), HandleRequestError> - where NumberFor: sc_finality_grandpa::BlockNumberOps, - { + ) -> Result<(), HandleRequestError> { let request = Request::::decode(&mut &payload[..])?; - let proof = WarpSyncProof::generate( - &*self.backend, - request.begin, - &self.authority_set.authority_set_changes(), - )?; + let EncodedProof(proof) = self.backend.generate(request.begin).map_err(HandleRequestError::InvalidRequest)?; pending_response.send(OutgoingResponse { - result: Ok(proof.encode()), + result: Ok(proof), reputation_changes: Vec::new(), sent_feedback: None, }).map_err(|_| HandleRequestError::SendResponse) } - /// Run [`GrandpaWarpSyncRequestHandler`]. - pub async fn run(mut self) - where NumberFor: sc_finality_grandpa::BlockNumberOps, - { + /// Run [`RequestHandler`]. + pub async fn run(mut self) { 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), + Ok(()) => debug!(target: "sync", "Handled grandpa warp sync request from {}.", peer), Err(e) => debug!( - target: LOG_TARGET, + target: "sync", "Failed to handle grandpa warp sync request from {}: {}", peer, e, ), @@ -160,7 +143,7 @@ impl> GrandpaWarpSyncRequestHandler>: block_request_protocol_config, state_request_protocol_config, light_client_request_protocol_config, + warp_sync: None, }).unwrap(); trace!(target: "test_network", "Peer identifier: {}", network.service().local_peer_id()); @@ -904,6 +905,7 @@ pub trait TestNetFactory: Sized where >: block_request_protocol_config, state_request_protocol_config, light_client_request_protocol_config, + warp_sync: None, }).unwrap(); self.mut_peers(|peers| { diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 0806d9a64bca3..d442586a18318 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -44,6 +44,7 @@ use sc_network::config::{Role, OnDemand}; use sc_network::NetworkService; use sc_network::block_request_handler::{self, BlockRequestHandler}; use sc_network::state_request_handler::{self, StateRequestHandler}; +use sc_network::warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider}; use sc_network::light_client_requests::{self, handler::LightClientRequestHandler}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ @@ -858,6 +859,8 @@ pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> { pub block_announce_validator_builder: Option) -> Box + Send> + Send >>, + /// An optional warp sync provider. + pub warp_sync: Option>>, } /// Build the network service, the network status sinks and an RPC sender. @@ -881,7 +884,7 @@ pub fn build_network( { let BuildNetworkParams { config, client, transaction_pool, spawn_handle, import_queue, on_demand, - block_announce_validator_builder, + block_announce_validator_builder, warp_sync, } = params; let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { @@ -932,6 +935,22 @@ pub fn build_network( } }; + let warp_sync_params = warp_sync.map(|provider| { + let protocol_config = if matches!(config.role, Role::Light) { + // Allow outgoing requests but deny incoming requests. + warp_request_handler::generate_request_response_config(protocol_id.clone()) + } else { + // Allow both outgoing and incoming requests. + let (handler, protocol_config) = WarpSyncRequestHandler::new( + protocol_id.clone(), + provider.clone(), + ); + spawn_handle.spawn("warp_sync_request_handler", handler.run()); + protocol_config + }; + (provider, protocol_config) + }); + let light_client_request_protocol_config = { if matches!(config.role, Role::Light) { // Allow outgoing requests but deny incoming requests. @@ -971,6 +990,7 @@ pub fn build_network( metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), block_request_protocol_config, state_request_protocol_config, + warp_sync: warp_sync_params, light_client_request_protocol_config, }; diff --git a/test-utils/test-runner/src/node.rs b/test-utils/test-runner/src/node.rs index 00be12b651bcc..cb77b742c02d3 100644 --- a/test-utils/test-runner/src/node.rs +++ b/test-utils/test-runner/src/node.rs @@ -147,6 +147,7 @@ impl Node { import_queue, on_demand: None, block_announce_validator_builder: None, + warp_sync: None, }; build_network(params)? }; From c1bd69b77e0aa899a5a55fe5ad1b77bb40fbcd8d Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 29 Jun 2021 10:15:56 +0200 Subject: [PATCH 04/20] Sync warp proofs first --- bin/node-template/node/src/service.rs | 4 +- bin/node/cli/src/service.rs | 4 +- client/cli/src/arg_enums.rs | 2 +- client/consensus/babe/src/lib.rs | 2 +- client/consensus/babe/src/verification.rs | 2 - client/consensus/epochs/src/lib.rs | 2 +- client/db/src/lib.rs | 1 + client/finality-grandpa/src/import.rs | 1 + client/finality-grandpa/src/lib.rs | 2 +- client/finality-grandpa/src/observer.rs | 3 +- .../src/{proof.rs => warp_proof.rs} | 106 ++++---------- client/informant/src/display.rs | 16 ++- client/network/src/lib.rs | 4 +- client/network/src/protocol.rs | 2 - client/network/src/protocol/sync.rs | 116 +++++++-------- client/network/src/protocol/sync/warp.rs | 134 ++++++------------ client/network/src/service.rs | 1 + client/network/src/warp_request_handler.rs | 10 +- 18 files changed, 150 insertions(+), 262 deletions(-) rename client/finality-grandpa/src/{proof.rs => warp_proof.rs} (81%) diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 42b628e0ef8d0..455de05fb1743 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -148,7 +148,7 @@ pub fn new_full(mut config: Configuration) -> Result } config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); - let warp_sync = Arc::new(sc_finality_grandpa::proof::NetworkProvider::new( + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), )); @@ -376,7 +376,7 @@ pub fn new_light(mut config: Configuration) -> Result }, )?; - let warp_sync = Arc::new(sc_finality_grandpa::proof::NetworkProvider::new( + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), )); diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index b267d81cdab65..bbbfac3bc0c3e 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -230,7 +230,7 @@ pub fn new_full_base( let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; config.network.extra_sets.push(grandpa::grandpa_peers_set_config()); - let warp_sync = Arc::new(grandpa::proof::NetworkProvider::new( + let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new( backend.clone(), import_setup.1.shared_authority_set().clone(), )); @@ -513,7 +513,7 @@ pub fn new_light_base( telemetry.as_ref().map(|x| x.handle()), )?; - let warp_sync = Arc::new(grandpa::proof::NetworkProvider::new( + let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), )); diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 51282e6a07e49..f7b7a5c347c26 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -243,7 +243,7 @@ arg_enum! { Fast, // Download blocks without executing them. Download latest state without proofs. FastUnsafe, - // Download the latest state + // Prove finality and download the latest state. Warp, } } diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 9c7147d696fb9..c0d61a568f0e5 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1309,7 +1309,7 @@ impl BabeBlockImport where let number = *block.header.number(); block.fork_choice = Some(ForkChoiceStrategy::Custom(true)); - // Reset block weight + // Reset block weight. aux_schema::write_block_weight( hash, 0, diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 22b88ddd9373c..469286f5110d7 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -219,7 +219,6 @@ fn check_secondary_plain_header( let author = &epoch.authorities[pre_digest.authority_index as usize].0; - println!("CHECKING EPOCH {}, slot={}", epoch.epoch_index, pre_digest.slot); if expected_author != author { return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())); } @@ -248,7 +247,6 @@ fn check_secondary_vrf_header( let author = &epoch.authorities[pre_digest.authority_index as usize].0; - println!("CHECKING SEC EPOCH {}, slot={}", epoch.epoch_index, pre_digest.slot); if expected_author != author { return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())); } diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 0e5fca0b0cc62..0a4830c7e1cbc 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -642,7 +642,7 @@ impl EpochChanges where &self.inner } - /// Reset to explicit specified pair of epochs. + /// Reset to a specified pair of epochs, as if they were announced at blocks `parent_hash` and `hash`. pub fn reset(&mut self, parent_hash: Hash, hash: Hash, number: Number, current: E, next: E) { self.inner = ForkTree::new(); self.epochs.clear(); diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 0585f678ca467..7141c2cae400f 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1525,6 +1525,7 @@ impl Backend { let cache = operation.old_state.into_cache_changes(); if finalized { + // TODO: ensure best chain contains this block. self.ensure_sequential_finalization(header, Some(last_finalized_hash))?; self.note_finalized( &mut transaction, diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index a8f71fd3e8539..17d480e295aad 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -455,6 +455,7 @@ where } } + /// Import whole new state and reset authority set. async fn import_state( &mut self, mut block: BlockImportParams>, diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 5c2a704afc796..737d0d867f5bb 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -120,7 +120,7 @@ mod notification; mod observer; mod until_imported; mod voting_rule; -pub mod proof; +pub mod warp_proof; pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet}; pub use aux_schema::best_justification; diff --git a/client/finality-grandpa/src/observer.rs b/client/finality-grandpa/src/observer.rs index 23c4f873a10b7..cd30b004557fa 100644 --- a/client/finality-grandpa/src/observer.rs +++ b/client/finality-grandpa/src/observer.rs @@ -175,12 +175,11 @@ where { let LinkHalf { client, - select_chain: _, persistent_data, voter_commands_rx, justification_sender, - justification_stream: _, telemetry, + .. } = link; let network = NetworkBridge::new( diff --git a/client/finality-grandpa/src/proof.rs b/client/finality-grandpa/src/warp_proof.rs similarity index 81% rename from client/finality-grandpa/src/proof.rs rename to client/finality-grandpa/src/warp_proof.rs index 59b80c3a951a3..929148b289d4b 100644 --- a/client/finality-grandpa/src/proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -22,7 +22,7 @@ use sp_runtime::codec::{Decode, Encode, self}; use sc_client_api::Backend as ClientBackend; use crate::{ find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification, - VoterSet, best_justification, SharedAuthoritySet, + best_justification, SharedAuthoritySet, }; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID}; @@ -31,7 +31,7 @@ use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT, NumberFor, One}, }; use sc_network::warp_request_handler::{ - WarpSyncProvider, EncodedProof, EncodedJustification, VerificationResult + WarpSyncProvider, EncodedProof, VerificationResult }; use std::sync::Arc; @@ -59,7 +59,7 @@ pub enum Error { pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 8 * 1024 * 1024; /// A proof of an authority set change. -#[derive(Decode, Encode)] +#[derive(Decode, Encode, Debug)] pub struct WarpSyncFragment { /// The last block that the given authority set finalized. This block should contain a digest /// signaling an authority set change from which we can fetch the next authority set. @@ -208,36 +208,23 @@ impl WarpSyncProof { &self, set_id: SetId, authorities: AuthorityList, - target_header: &Block::Header, - target_justification: &[u8], - ) -> Result, String> + ) -> Result<(SetId, AuthorityList), Error> where NumberFor: BlockNumberOps, { let mut current_set_id = set_id; let mut current_authorities = authorities; - let mut current_hash = Default::default(); - let target_hash = target_header.hash(); for (fragment_num, proof) in self.proofs.iter().enumerate() { - if proof.justification.target().0 > *target_header.number() { - Self::verify_justification( - target_justification, - (target_hash.clone(), *target_header.number()), - current_set_id, - ¤t_authorities, - ).map_err(|e| format!("Target block verification error: {:?}", e))?; - return Ok(VerificationResult::::Complete(current_set_id, current_authorities)); - } - proof .justification .verify(current_set_id, ¤t_authorities) - .map_err(|err| err.to_string())?; + .map_err(|err| Error::InvalidProof(err.to_string()))?; - current_hash = proof.header.hash(); - if proof.justification.target().1 != current_hash { - return Err("Mismatch between header and justification".to_owned()); + if proof.justification.target().1 != proof.header.hash() { + return Err(Error::InvalidProof( + "Mismatch between header and justification".to_owned() + )); } if let Some(scheduled_change) = find_scheduled_change::(&proof.header) { @@ -246,47 +233,12 @@ impl WarpSyncProof { } else if fragment_num != self.proofs.len() - 1 || !self.is_finished { // Only the last fragment of the last proof is allowed to be missing the authority // set change. - return Err("Header is missing authority set change digest".to_string()); - } - - if current_hash == target_hash { - return Ok(VerificationResult::::Complete(current_set_id, current_authorities)) + return Err(Error::InvalidProof( + "Header is missing authority set change digest".to_string(), + )); } } - - if self.is_finished { - Self::verify_justification( - target_justification, - (target_hash, *target_header.number()), - current_set_id, - ¤t_authorities, - ).map_err(|e| format!("Target block verification error: {:?}", e))?; - Ok(VerificationResult::::Complete(current_set_id, current_authorities)) - } else { - Ok(VerificationResult::::Partial(current_set_id, current_authorities, current_hash)) - } - } - - /// Verifies the block finality justification against given authority set. - fn verify_justification( - encoded_justification: &[u8], - finalized_target: (Block::Hash, NumberFor), - set_id: u64, - authorities: &AuthorityList, - ) -> Result<(), Error> - where - NumberFor: BlockNumberOps, - { - let voters = VoterSet::new(authorities.iter().cloned()) - .ok_or_else(|| Error::InvalidProof("Can't create voter set".to_string()))?; - - GrandpaJustification::::decode_and_verify_finalizes( - encoded_justification, - finalized_target, - set_id, - &voters - ).map_err(|err| Error::InvalidProof(err.to_string()))?; - Ok(()) + Ok((current_set_id, current_authorities)) } } @@ -333,15 +285,24 @@ where proof: &EncodedProof, set_id: SetId, authorities: AuthorityList, - target_header: &Block::Header, - target_justification: &EncodedJustification, ) -> Result, String> { let EncodedProof(proof) = proof; - let EncodedJustification(target_justification) = target_justification; let proof = WarpSyncProof::::decode(&mut proof.as_slice()) .map_err(|e| format!("Proof decoding error: {:?}", e))?; - proof.verify(set_id, authorities, target_header, target_justification.as_slice()) + let last_header = proof.proofs.last().map(|p| p.header.clone()) + .ok_or_else(|| "Empty proof".to_string())?; + let (next_set_id, next_authorities) = proof.verify(set_id, authorities) + .map_err(|e| format!("Proof verification error: {:?}", e))?; + if proof.is_finished { + Ok(VerificationResult::::Complete(next_set_id, next_authorities, last_header)) + } else { + Ok(VerificationResult::::Partial(next_set_id, next_authorities, last_header.hash())) + } + } + + fn current_authorities(&self) -> AuthorityList { + self.authority_set.inner().current_authorities.clone() } } @@ -362,7 +323,6 @@ mod tests { ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, }; - use sc_network::warp_request_handler::VerificationResult; #[test] fn warp_sync_proof_generate_verify() { @@ -377,7 +337,6 @@ mod tests { let mut current_authorities = vec![Ed25519Keyring::Alice]; let mut current_set_id = 0; let mut authority_set_changes = Vec::new(); - let mut last_block = None; for n in 1..=100 { let mut block = client @@ -419,7 +378,6 @@ mod tests { block.header.digest_mut().logs.push(digest); } - let header = block.header.clone(); futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); if let Some(new_authorities) = new_authorities { @@ -469,7 +427,6 @@ mod tests { current_set_id += 1; current_authorities = new_authorities; - last_block = Some((header, justification)); } } @@ -481,18 +438,11 @@ mod tests { let warp_sync_proof = WarpSyncProof::generate(&*backend, genesis_hash, &authority_set_changes).unwrap(); - let (last_header, last_justification) = last_block.unwrap(); - // verifying the proof should yield the last set id and authorities - let (new_set_id, new_authorities) = match warp_sync_proof.verify( + let (new_set_id, new_authorities) = warp_sync_proof.verify( 0, genesis_authorities, - &last_header, - &last_justification.encode(), - ).unwrap() { - VerificationResult::Complete(new_set_id, new_authorities) => (new_set_id, new_authorities), - _ => panic!("Unexpected result"), - }; + ).unwrap(); let expected_authorities = current_authorities .iter() diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 0b7f8bcfaf16b..c1334557c2437 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -96,16 +96,22 @@ impl InformantDisplay { let (level, status, target) = match ( net_status.sync_state, net_status.best_seen_block, - net_status.state_sync + net_status.state_sync, + net_status.warp_sync, ) { - (_, _, Some(state)) => ( + (_, _, _, Some(warp)) => ( + "⏩", + "Warp sync".into(), + format!(", {}, ({:.2}) Mib", warp.phase, (warp.total_bytes as f32) / (1024f32 * 1024f32)), + ), + (_, _, Some(state), _) => ( "⚙️ ", "Downloading state".into(), format!(", {}%, ({:.2}) Mib", state.percentage, (state.size as f32) / (1024f32 * 1024f32)), ), - (SyncState::Idle, _, _) => ("💤", "Idle".into(), "".into()), - (SyncState::Downloading, None, _) => ("⚙️ ", format!("Preparing{}", speed), "".into()), - (SyncState::Downloading, Some(n), None) => ( + (SyncState::Idle, _, _, _) => ("💤", "Idle".into(), "".into()), + (SyncState::Downloading, None, _, _) => ("⚙️ ", format!("Preparing{}", speed), "".into()), + (SyncState::Downloading, Some(n), None, _) => ( "⚙️ ", format!("Syncing{}", speed), format!(", target=#{}", n), diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 0d544d30f1f55..72d327fa2cf7d 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -271,7 +271,7 @@ pub mod transactions; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; pub use protocol::{event::{DhtEvent, Event, ObservedRole}, PeerInfo}; -pub use protocol::sync::{SyncState, StateDownloadProgress}; +pub use protocol::sync::{SyncState, StateDownloadProgress, WarpSyncProgress, WarpSyncPhase}; pub use service::{ NetworkService, NetworkWorker, RequestFailure, OutboundFailure, NotificationSender, NotificationSenderReady, IfDisconnected, @@ -326,4 +326,6 @@ pub struct NetworkStatus { pub total_bytes_outbound: u64, /// State sync in progress. pub state_sync: Option, + /// Warp sync in progress. + pub warp_sync: Option, } diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index f27942025c6f5..3b9b411a22bd8 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -674,7 +674,6 @@ impl Protocol { Ok(sync::OnBlockData::Request(peer, req)) => { self.prepare_block_request(peer, req) } - Ok(sync::OnBlockData::Ignore) => CustomMessageOutcome::None, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); @@ -1012,7 +1011,6 @@ impl Protocol { Ok(sync::OnBlockData::Request(peer, req)) => { self.prepare_block_request(peer, req) }, - Ok(sync::OnBlockData::Ignore) => CustomMessageOutcome::None, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 3f7dd732111b3..ee57d7b7d8592 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -297,8 +297,6 @@ pub enum PeerSyncState { DownloadingJustification(B::Hash), /// Downloading state. DownloadingState, - /// Downloading warp block. - DownloadingWarpBlock, /// Downloading warp proof. DownloadingWarpProof, } @@ -327,6 +325,40 @@ pub struct StateDownloadProgress { pub size: u64, } + +/// Reported warp sync phase. +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum WarpSyncPhase { + /// Waiting for peers to connect. + AwaitingPeers, + /// Downloading and verifying grandpa warp proofs. + DownloadingWarpProofs, + /// Downloading state data. + DownloadingState, + /// Importing state. + ImportingState, +} + +impl fmt::Display for WarpSyncPhase { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WarpSyncPhase::AwaitingPeers => write!(f, "Waiting for peers"), + WarpSyncPhase::DownloadingWarpProofs => write!(f, "Downloading finality proofs"), + WarpSyncPhase::DownloadingState => write!(f, "Downloading state"), + WarpSyncPhase::ImportingState => write!(f, "Importing state"), + } + } +} + +/// Reported warp sync progress. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct WarpSyncProgress { + /// Estimated download percentage. + pub phase: WarpSyncPhase, + /// Total bytes downloaded so far. + pub total_bytes: u64, +} + /// Syncing status and statistics. #[derive(Clone)] pub struct Status { @@ -341,7 +373,7 @@ pub struct Status { /// State sync status in progress, if any. pub state_sync: Option, /// Warp sync in progress, if any. - pub warp_sync: Option, + pub warp_sync: Option, } /// A peer did not behave as expected and should be reported. @@ -363,8 +395,6 @@ pub enum OnBlockData { Import(BlockOrigin, Vec>), /// A new block request needs to be made to the given peer. Request(PeerId, BlockRequest), - /// Block is processed internally and no furter action is requried. - Ignore, } impl OnBlockData { @@ -391,7 +421,7 @@ pub enum OnStateData { /// Result of [`ChainSync::on_warp_sync_data`]. #[derive(Debug)] pub enum OnWarpSyncData { - /// The block and state that should be imported. + /// Warp proof request is issued. WarpProofRequest(PeerId, warp::WarpProofRequest), /// A new state request needs to be made to the given peer. StateRequest(PeerId, StateRequest) @@ -583,13 +613,21 @@ impl ChainSync { SyncState::Idle }; + let warp_sync_progress = match (&self.warp_sync, &self.mode) { + (None, SyncMode::Warp) => Some(WarpSyncProgress { + phase: WarpSyncPhase::AwaitingPeers, + total_bytes: 0 + }), + (Some(sync), _) => Some(sync.progress()), + _ => None, + }; Status { state: sync_state, best_seen_block: best_seen, num_peers: self.peers.len() as u32, queued_blocks: self.queue_blocks.len() as u32, state_sync: self.state_sync.as_ref().map(|s| s.progress()), - warp_sync: self.warp_sync.as_ref().map(|s| s.progress()), + warp_sync: warp_sync_progress, } } @@ -648,14 +686,9 @@ impl ChainSync { if let SyncMode::Warp = &self.mode { // TODO: wait for 3 peers if self.warp_sync.is_none() { - let number = best_number - 16u32.into(); - log::debug!( - target: "sync", - "Starting warp state sync for #{}", - number, - ); + log::debug!(target: "sync", "Starting warp state sync."); if let Some(provider) = &self.warp_sync_provider { - self.warp_sync = Some(WarpSync::new(self.client.clone(), provider.clone(), number)); + self.warp_sync = Some(WarpSync::new(self.client.clone(), provider.clone())); } } } @@ -820,45 +853,12 @@ impl ChainSync { /// Get an iterator over all block requests of all peers. pub fn block_requests(&mut self) -> impl Iterator)> + '_ { - if let Some(warp_sync) = &self.warp_sync { - if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpBlock) { - // Only one pending warp request is allowed. - return Either::Left(Either::Left(std::iter::empty())) - } - if let Some(request) = warp_sync.next_block_request() { - // Find a peer that can handle the request. - let peer = self.peers.iter_mut().find_map(move |(id, peer)| { - if !peer.state.is_available() { - return None - } - - if peer.best_number >= warp_sync.target_block_number() { - peer.state = PeerSyncState::DownloadingWarpBlock; - Some((id, peer.best_number)) - } else { - None - } - }); - if let Some((peer, best_num)) = peer { - trace!( - target: "sync", - "New warp block request for {}, (best:{}, target:{}) {:?}", - peer, - best_num, - warp_sync.target_block_number(), - request, - ); - return Either::Left(Either::Right(std::iter::once((peer, request)))); - } - } - return Either::Left(Either::Left(std::iter::empty())) - } - if self.pending_requests.is_empty() || self.state_sync.is_some() { - return Either::Left(Either::Left(std::iter::empty())) + if self.pending_requests.is_empty() || self.state_sync.is_some() || self.warp_sync.is_some() { + return Either::Left(std::iter::empty()) } if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { trace!(target: "sync", "Too many blocks in the queue."); - return Either::Left(Either::Left(std::iter::empty())) + return Either::Left(std::iter::empty()) } let major_sync = self.status().state == SyncState::Downloading; let attrs = self.required_block_attributes(); @@ -1011,19 +1011,6 @@ impl ChainSync { request: Option>, response: BlockResponse ) -> Result, BadPeer> { - if let Some(sync) = &mut self.warp_sync { - if let Some(peer) = self.peers.get_mut(who) { - peer.state = PeerSyncState::Available; - } - let result = sync.import_block(response); - return match result { - warp::WarpBlockImportResult::WarpProofRequest(_) => { - trace!(target: "sync", "Successfully imported warp header from {}", who); - Ok(OnBlockData::Ignore) - }, - warp::WarpBlockImportResult::BadResponse => Err(BadPeer(who.clone(), rep::NO_BLOCK)), - } - } self.downloaded_blocks += response.blocks.len(); let new_blocks: Vec> = if let Some(peer) = self.peers.get_mut(who) { @@ -1179,7 +1166,6 @@ impl ChainSync { | PeerSyncState::DownloadingJustification(..) | PeerSyncState::DownloadingState | PeerSyncState::DownloadingWarpProof - | PeerSyncState::DownloadingWarpBlock => Vec::new() } } else { @@ -1470,7 +1456,7 @@ impl ChainSync { info!( target: "sync", "Warp sync is complete ({} MiB), restarting block sync.", - self.warp_sync.as_ref().map_or(0, |s| s.progress().size / (1024 * 1024)), + self.warp_sync.as_ref().map_or(0, |s| s.progress().total_bytes / (1024 * 1024)), ); self.warp_sync = None; self.mode = SyncMode::Full; diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 4af2e0031fe2c..2284cfe9e564d 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -17,28 +17,23 @@ // along with this program. If not, see . use std::sync::Arc; -use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; +use sp_runtime::traits::{Block as BlockT, NumberFor, Header, Zero}; use sp_finality_grandpa::{SetId, AuthorityList}; use crate::schema::v1::{StateRequest, StateResponse}; use crate::chain::Client; -use crate::protocol::message; use super::state::StateSync; -use crate::StateDownloadProgress; +use crate::{WarpSyncProgress, WarpSyncPhase}; pub use crate::warp_request_handler::{ Request as WarpProofRequest, WarpSyncProvider, EncodedProof, VerificationResult, - EncodedJustification, }; pub use super::state::ImportResult; /// Warp sync support. enum Phase { - Block, WarpProof { set_id: SetId, authorities: AuthorityList, - target_justification: EncodedJustification, - target_header: B::Header, last_hash: B::Hash, }, State(StateSync), @@ -54,20 +49,14 @@ pub enum WarpProofImportResult { BadResponse, } -pub enum WarpBlockImportResult { - /// Start downloading state data. - WarpProofRequest(WarpProofRequest), - /// Bad block data. - BadResponse, -} - -/// Warp sync state machine. Accumulates target header, warp proofs, state. +/// Warp sync state machine. Accumulates warp proofs and state. pub struct WarpSync { target_hash: B::Hash, target_num: NumberFor, phase: Phase, client: Arc>, warp_sync_provider: Arc>, + total_proof_bytes: u64, } impl WarpSync { @@ -75,21 +64,28 @@ impl WarpSync { pub fn new( client: Arc>, warp_sync_provider: Arc>, - target_num: NumberFor ) -> Self { + let last_hash = client.hash(Zero::zero()).unwrap() + .expect("Genesis header always exists"); + let phase = Phase::WarpProof { + set_id: 0, + authorities: warp_sync_provider.current_authorities(), + last_hash, + }; WarpSync { client, warp_sync_provider, target_hash: Default::default(), - target_num, - phase: Phase::Block, + target_num: Zero::zero(), + phase, + total_proof_bytes: 0, } } /// Validate and import a state reponse. pub fn import_state(&mut self, response: StateResponse) -> ImportResult { match &mut self.phase { - Phase::Block | Phase::WarpProof {..} => { + Phase::WarpProof {..} => { log::debug!(target: "sync", "Unexpected state response"); return ImportResult::BadResponse; } @@ -100,32 +96,36 @@ impl WarpSync { /// Validate and import a warp proof reponse. pub fn import_warp_proof(&mut self, response: EncodedProof) -> WarpProofImportResult { match &mut self.phase { - Phase::Block | Phase::State(_) => { + Phase::State(_) => { log::debug!(target: "sync", "Unexpected warp proof response"); WarpProofImportResult::BadResponse }, - Phase::WarpProof { set_id, authorities, target_justification, target_header, last_hash } => { + Phase::WarpProof { set_id, authorities, last_hash } => { match self.warp_sync_provider.verify( &response, *set_id, std::mem::take(authorities), - target_header, - target_justification, ) { Err(e) => { - log::debug!( target: "sync", "Bad warp proof response: {:?}", e); + log::debug!(target: "sync", "Bad warp proof response: {:?}", e); return WarpProofImportResult::BadResponse; }, Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => { + log::debug!(target: "sync", "Verified partial proof, set_id={:?}", new_set_id); *set_id = new_set_id; *authorities = new_authorities; *last_hash = new_last_hash.clone(); + self.total_proof_bytes += response.0.len() as u64; WarpProofImportResult::WarpProofRequest(WarpProofRequest { begin: new_last_hash }) }, - Ok(VerificationResult::Complete(_, _)) => { - let state_sync = StateSync::new(self.client.clone(), target_header.clone(), false); + Ok(VerificationResult::Complete(new_set_id, _, header)) => { + log::debug!(target: "sync", "Verified complete proof, set_id={:?}", new_set_id); + self.target_hash = header.hash(); + self.target_num = *header.number(); + self.total_proof_bytes += response.0.len() as u64; + let state_sync = StateSync::new(self.client.clone(), header, false); let request = state_sync.next_request(); self.phase = Phase::State(state_sync); WarpProofImportResult::StateRequest(request) @@ -135,69 +135,10 @@ impl WarpSync { } } - /// Validate and import a block reponse. - pub fn import_block(&mut self, mut response: message::BlockResponse) -> WarpBlockImportResult { - match &mut self.phase { - Phase::Block => { - if response.blocks.len() != 1 { - log::debug!( target: "sync", "Bad block response"); - return WarpBlockImportResult::BadResponse; - } - let (header, justification) = match response.blocks.pop() - .map(|r| (r.header, r.justification)) - { - Some((Some(header), Some(justification))) => (header, justification), - _ => { - log::debug!( target: "sync", "No header or justification in the response"); - return WarpBlockImportResult::BadResponse; - } - }; - self.target_hash = header.hash(); - log::debug!( target: "sync", "Warp header received."); - - //TODO: error here - let last_hash = self.client.hash(Zero::zero()).unwrap() - .expect("Genesis header always exists"); - self.phase = Phase::WarpProof { - set_id: 0, - authorities: AuthorityList::default(), - target_justification: EncodedJustification(justification), - target_header: header, - last_hash: last_hash.clone(), - }; - - let request = WarpProofRequest { begin: last_hash }; - WarpBlockImportResult::WarpProofRequest(request) - } - Phase::State(_) | Phase::WarpProof {..} => { - log::debug!( - target: "sync", - "Unexpected block response", - ); - WarpBlockImportResult::BadResponse - } - } - } - - /// Produce next state request. - pub fn next_block_request(&self) -> Option> { - match &self.phase { - Phase::Block => Some(message::generic::BlockRequest { - id: 0, - fields: message::BlockAttributes::HEADER, - from: message::FromBlock::Number(self.target_num), - to: None, - direction: message::Direction::Ascending, - max: Some(1) - }), - Phase::State(_) | Phase::WarpProof {..} => None, - } - } - /// Produce next state request. pub fn next_state_request(&self) -> Option { match &self.phase { - Phase::Block | Phase::WarpProof {..} => None, + Phase::WarpProof {..} => None, Phase::State(sync) => Some(sync.next_request()) } } @@ -205,7 +146,7 @@ impl WarpSync { /// Produce next warp proof request. pub fn next_warp_poof_request(&self) -> Option> { match &self.phase { - Phase::Block | Phase::State(_) => None, + Phase::State(_) => None, Phase::WarpProof { last_hash, .. } => { Some(WarpProofRequest { begin: last_hash.clone(), @@ -227,19 +168,26 @@ impl WarpSync { /// Check if the state is complete. pub fn is_complete(&self) -> bool { match &self.phase { - Phase::Block | Phase::WarpProof {..} => false, + Phase::WarpProof {..} => false, Phase::State(sync) => sync.is_complete(), } } /// Returns state sync estimated progress (percentage, bytes) - pub fn progress(&self) -> StateDownloadProgress { + pub fn progress(&self) -> WarpSyncProgress { match &self.phase { - Phase::Block | Phase::WarpProof {..} => StateDownloadProgress { - percentage: 0, - size: 0, + Phase::WarpProof {..} => WarpSyncProgress { + phase: WarpSyncPhase::DownloadingWarpProofs, + total_bytes: self.total_proof_bytes, }, - Phase::State(sync) => sync.progress(), + Phase::State(sync) => WarpSyncProgress { + phase: if self.is_complete() { + WarpSyncPhase::ImportingState + } else { + WarpSyncPhase::DownloadingState + }, + total_bytes: self.total_proof_bytes + sync.progress().size, + } } } } diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 8e046930ab2f9..8a81c54b5c4d9 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -462,6 +462,7 @@ impl NetworkWorker { total_bytes_inbound: self.total_bytes_inbound(), total_bytes_outbound: self.total_bytes_outbound(), state_sync: status.state_sync, + warp_sync: status.warp_sync, } } diff --git a/client/network/src/warp_request_handler.rs b/client/network/src/warp_request_handler.rs index 4a4b0da7b6bcb..172ab5c3551e7 100644 --- a/client/network/src/warp_request_handler.rs +++ b/client/network/src/warp_request_handler.rs @@ -26,10 +26,8 @@ use sp_finality_grandpa::{SetId, AuthorityList}; use std::time::Duration; use std::sync::Arc; -/// Scale-encided warp sync proof response +/// Scale-encoded warp sync proof response. pub struct EncodedProof(pub Vec); -/// Scale-encided warp sync proof -pub struct EncodedJustification(pub Vec); /// Warp sync request #[derive(Encode, Decode, Debug)] @@ -45,7 +43,7 @@ pub enum VerificationResult { /// Proof is valid, but the target was not reached. Partial(SetId, AuthorityList, Block::Hash), /// Target finality is proved. - Complete(SetId, AuthorityList), + Complete(SetId, AuthorityList, Block::Header), } /// Warp sync backend. Handles retrieveing and verifying warp sync proofs. @@ -58,9 +56,9 @@ pub trait WarpSyncProvider: Send + Sync { proof: &EncodedProof, set_id: SetId, authorities: AuthorityList, - target_header: &B::Header, - target_justification: &EncodedJustification, ) -> Result, String>; + /// Get current list of authorities. This is supposed to be genesis authorities when starting sync. + fn current_authorities(&self) -> AuthorityList; } /// Generates a [`RequestResponseConfig`] for the grandpa warp sync request protocol, refusing incoming requests. From e5991c14c0d85f292441cbc1052d887cf14c73cd Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 15 Jul 2021 18:19:52 +0200 Subject: [PATCH 05/20] Added basic documentation --- client/network/README.md | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/client/network/README.md b/client/network/README.md index 914720f53e2a9..9522bd26b1c35 100644 --- a/client/network/README.md +++ b/client/network/README.md @@ -203,6 +203,69 @@ integer representing the role of the node: In the future, though, these restrictions will be removed. +# Sync + +The crate implements a number of syncing algorithms. The main purpose of the syncing algorithm is +get the chain to the latest state and keep it synced with the rest of the network by downloading and +importing new data as soon as it becomes available. Once the node starts it catches up with the network +with one of the initial sync methods listed below, and once it is completed uses a keep-up sync to +download new blocks. + +## Full and light sync + +This is the default syncing method for the initial and keep-up sync. The algorithm starts with the +current best block and downloads block data progressively from multiple peers if available. Once +there's a sequence of blocks ready to be imported they are fed to the import queue. Full nodes download +and execute full blocks, while light nodes only download and import headers. This continues until each peers +has no more new blocks to give. + +For each peer the sync maintains the number of our common best block with that peer. This number is updates +whenever peer announce new blocks or our best block advances. This allows to keep track of peers that have new +block data and request new information as soon as it is announced. In keep-up mode, we also track peers that +announce blocks on all branches and not just the best branch. The sync algorithm tries to be greedy and download +All data that's announced. + +## Fast sync + +In this mode the initial downloads and verifies full header history. This allows to validate +authority set transitions and arrive at a recent header. After header chain is verified and imported +the node starts downloading a state snapshot using the state request protocol. Each `StateRequest` +contains a starting storage key, which is empty for the first request. +`StateResponse` contains a storage proof for a sequence of keys and values in the storage +starting (but not including) from the key that is in the request. After iterating the proof trie against +the storage root that is in the target header, the node issues The next `StateRequest` with set starting +key set to the last key from the previous response. This continues until trie iteration reaches the end. +The state is then imported into the database and the keep-up sync starts in normal full/light sync mode. + +## Warp sync + +This is similar to fast sync, but instead of downloading and verifying full header chain, the algorithm +only downloads finalized authority set changes. + +### GRANDPA warp sync. + +GRANDPA keeps justifications for each finalized authority set change. Each change is signed by the +authorities from the previous set. By downloading and verifying these signed hand-offs starting from genesis, +we arrive at a recent header faster than downloading full header chain. Each `WarpSyncRequest` contains a block +hash to a to start collecting proofs from. `WarpSyncResponse` contains a sequence of block headers and +justifications. The proof downloader checks the justifications and continues requesting proofs from the last +header hash, until it arrives at some recent header. + +Once the finality chain is proved for a header, the state matching the header is downloaded much like during +the fast sync. The state is verified to match the header storage root. After the state is imported into the +database it is queried for the information that allows GRANDPA and BABE to continue operating from that state. +This includes BABE epoch information and GRANDPA authority set id. + +### Background block download. + +After the latest state has been imported the node is fully operational, but is still missing historic block +data. I.e. it is unable to serve bock bodies and headers other than the most recent one. To make sure all +nodes have block history available, a background sync process is started that downloads all the missing blocks. +It is run in parallel with the keep-up sync and does not interfere with downloading of the recent blocks. +During this download we also import GRANPA justifications for blocks with authority set changes, so that +The warp-synced node has all the data to serve for other nodes nodes that might want to sync from it with +any method. + # Usage Using the `sc-network` crate is done through the [`NetworkWorker`] struct. Create this From 61ecbd5ed4210c08c4c0fe7e10dff1c540ede726 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 17 Jul 2021 10:43:41 +0200 Subject: [PATCH 06/20] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- client/consensus/babe/src/lib.rs | 4 ++-- client/informant/src/display.rs | 2 +- primitives/consensus/common/src/import_queue.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index c0d61a568f0e5..cdce36fd09fe5 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1129,7 +1129,7 @@ where let parent_hash = *block.header.parent_hash(); if block.with_state() { - // When importting whole state we don't calculate epoch descriptor, but rater + // When importing whole state we don't calculate epoch descriptor, but rather // read it from the state after import. return Ok((block, Default::default())) } @@ -1293,7 +1293,7 @@ impl BabeBlockImport where Inner: BlockImport> + Send + Sync, Inner::Error: Into, Client: HeaderBackend + HeaderMetadata - + AuxStore + ProvideRuntimeApi + ProvideCache + Send + Sync, + + AuxStore + ProvideRuntimeApi + ProvideCache + Send + Sync, Client::Api: BabeApi + ApiExt, { /// Import whole state after warp sync. diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index c1334557c2437..24b60df15febe 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -101,7 +101,7 @@ impl InformantDisplay { ) { (_, _, _, Some(warp)) => ( "⏩", - "Warp sync".into(), + "Warping".into(), format!(", {}, ({:.2}) Mib", warp.phase, (warp.total_bytes as f32) / (1024f32 * 1024f32)), ), (_, _, Some(state), _) => ( diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index 3d8de48d3af72..fc932586062f6 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -247,6 +247,7 @@ pub(crate) async fn import_single_block_metered, Trans import_block.justifications = justifications; import_block.post_hash = Some(hash); import_block.import_existing = block.import_existing; + if let Some(state) = block.state { import_block.state_action = StateAction::ApplyChanges(crate::StorageChanges::Import(state)); } else if block.skip_execution { From 61eb22a5219a87f7ea42cfaeeb04332d7cd1c224 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 17 Jul 2021 10:48:26 +0200 Subject: [PATCH 07/20] Style changes --- client/consensus/babe/src/lib.rs | 4 +- client/finality-grandpa/src/import.rs | 3 +- client/finality-grandpa/src/warp_proof.rs | 4 +- test-utils/test-runner/src/client.rs | 338 +++++++++++----------- 4 files changed, 176 insertions(+), 173 deletions(-) diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index cdce36fd09fe5..155b640e814c9 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1130,7 +1130,9 @@ where if block.with_state() { // When importing whole state we don't calculate epoch descriptor, but rather - // read it from the state after import. + // read it from the state after import. We also skip all verifications + // because there's no parent state and we trust the sync module to verify + // that the state is correct and finalized. return Ok((block, Default::default())) } diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 17d480e295aad..4a4215ab80c6f 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -468,7 +468,8 @@ where let import_result = (&*self.inner).import_block(block, new_cache).await; match import_result { Ok(ImportResult::Imported(aux)) => { - // Read authority list and set id from the state. + // We've just imported a new state. We trust the sync module that the state + // is correct and final. So we can read the authority list and set id from the state. self.authority_set_hard_forks.clear(); let block_id = BlockId::hash(hash); let authorities = self.inner.runtime_api().grandpa_authorities(&block_id) diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/finality-grandpa/src/warp_proof.rs index 929148b289d4b..9abb4728e37e0 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -231,8 +231,8 @@ impl WarpSyncProof { current_authorities = scheduled_change.next_authorities; current_set_id += 1; } else if fragment_num != self.proofs.len() - 1 || !self.is_finished { - // Only the last fragment of the last proof is allowed to be missing the authority - // set change. + // Only the last fragment of the last proof message is allowed to be missing + // the authority set change. return Err(Error::InvalidProof( "Header is missing authority set change digest".to_string(), )); diff --git a/test-utils/test-runner/src/client.rs b/test-utils/test-runner/src/client.rs index c34abdd9a2f39..8b1e5cd4c1b1e 100644 --- a/test-utils/test-runner/src/client.rs +++ b/test-utils/test-runner/src/client.rs @@ -29,8 +29,8 @@ use jsonrpc_core::MetaIoHandler; use manual_seal::{run_manual_seal, EngineCommand, ManualSealParams, import_queue, rpc::{ManualSeal, ManualSealApi}}; use sc_client_api::backend::Backend; use sc_service::{ - build_network, spawn_tasks, BuildNetworkParams, SpawnTasksParams, TFullBackend, - TFullClient, TaskManager, new_full_parts, Configuration, ChainSpec, TaskExecutor, + build_network, spawn_tasks, BuildNetworkParams, SpawnTasksParams, TFullBackend, + TFullClient, TaskManager, new_full_parts, Configuration, ChainSpec, TaskExecutor, }; use sc_transaction_pool::BasicPool; use sc_transaction_pool_api::TransactionPool; @@ -42,181 +42,181 @@ use sp_offchain::OffchainWorkerApi; use std::sync::Arc; type ClientParts = ( - Arc>, - TaskManager, - Arc::Block, ::RuntimeApi, ::Executor>>, - Arc::Block, - Hash = <::Block as BlockT>::Hash, - Error = sc_transaction_pool::error::Error, - InPoolTransaction = sc_transaction_pool::Transaction< - <::Block as BlockT>::Hash, - <::Block as BlockT>::Extrinsic, - >, - >>, - mpsc::Sender::Block as BlockT>::Hash>>, - Arc::Block>>, + Arc>, + TaskManager, + Arc::Block, ::RuntimeApi, ::Executor>>, + Arc::Block, + Hash = <::Block as BlockT>::Hash, + Error = sc_transaction_pool::error::Error, + InPoolTransaction = sc_transaction_pool::Transaction< + <::Block as BlockT>::Hash, + <::Block as BlockT>::Extrinsic, + >, + >>, + mpsc::Sender::Block as BlockT>::Hash>>, + Arc::Block>>, ); /// Provide the config or chain spec for a given chain pub enum ConfigOrChainSpec { - /// Configuration object - Config(Configuration), - /// Chain spec object - ChainSpec(Box, TaskExecutor) + /// Configuration object + Config(Configuration), + /// Chain spec object + ChainSpec(Box, TaskExecutor) } /// Creates all the client parts you need for [`Node`] pub fn client_parts(config_or_chain_spec: ConfigOrChainSpec) -> Result, sc_service::Error> - where - T: ChainInfo + 'static, - >>::RuntimeApi: - Core + Metadata + OffchainWorkerApi + SessionKeys - + TaggedTransactionQueue + BlockBuilder + BabeApi - + ApiExt as Backend>::State> + where + T: ChainInfo + 'static, + >>::RuntimeApi: + Core + Metadata + OffchainWorkerApi + SessionKeys + + TaggedTransactionQueue + BlockBuilder + BabeApi + + ApiExt as Backend>::State> + GrandpaApi, - ::Call: From>, - <::Block as BlockT>::Hash: FromStr, - <<::Block as BlockT>::Header as Header>::Number: num_traits::cast::AsPrimitive, + ::Call: From>, + <::Block as BlockT>::Hash: FromStr, + <<::Block as BlockT>::Header as Header>::Number: num_traits::cast::AsPrimitive, { - use sp_consensus_babe::AuthorityId; - let config = match config_or_chain_spec { - ConfigOrChainSpec::Config(config) => config, - ConfigOrChainSpec::ChainSpec(chain_spec, task_executor) => { - default_config(task_executor, chain_spec) - }, - }; - - let (client, backend, keystore, mut task_manager) = - new_full_parts::(&config, None)?; - let client = Arc::new(client); - - let select_chain = sc_consensus::LongestChain::new(backend.clone()); - - let (grandpa_block_import, ..) = - grandpa::block_import(client.clone(), &(client.clone() as Arc<_>), select_chain.clone(), None)?; - - let slot_duration = sc_consensus_babe::Config::get_or_compute(&*client)?; - let (block_import, babe_link) = sc_consensus_babe::block_import( - slot_duration.clone(), - grandpa_block_import, - client.clone(), - )?; - - let consensus_data_provider = BabeConsensusDataProvider::new( - client.clone(), - keystore.sync_keystore(), - babe_link.epoch_changes().clone(), - vec![(AuthorityId::from(Alice.public()), 1000)], - ) - .expect("failed to create ConsensusDataProvider"); - - let import_queue = - import_queue(Box::new(block_import.clone()), &task_manager.spawn_essential_handle(), None); - - let transaction_pool = BasicPool::new_full( - config.transaction_pool.clone(), - true.into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let (network, system_rpc_tx, network_starter) = { - let params = BuildNetworkParams { - config: &config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - spawn_handle: task_manager.spawn_handle(), - import_queue, - on_demand: None, - block_announce_validator_builder: None, + use sp_consensus_babe::AuthorityId; + let config = match config_or_chain_spec { + ConfigOrChainSpec::Config(config) => config, + ConfigOrChainSpec::ChainSpec(chain_spec, task_executor) => { + default_config(task_executor, chain_spec) + }, + }; + + let (client, backend, keystore, mut task_manager) = + new_full_parts::(&config, None)?; + let client = Arc::new(client); + + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let (grandpa_block_import, ..) = + grandpa::block_import(client.clone(), &(client.clone() as Arc<_>), select_chain.clone(), None)?; + + let slot_duration = sc_consensus_babe::Config::get_or_compute(&*client)?; + let (block_import, babe_link) = sc_consensus_babe::block_import( + slot_duration.clone(), + grandpa_block_import, + client.clone(), + )?; + + let consensus_data_provider = BabeConsensusDataProvider::new( + client.clone(), + keystore.sync_keystore(), + babe_link.epoch_changes().clone(), + vec![(AuthorityId::from(Alice.public()), 1000)], + ) + .expect("failed to create ConsensusDataProvider"); + + let import_queue = + import_queue(Box::new(block_import.clone()), &task_manager.spawn_essential_handle(), None); + + let transaction_pool = BasicPool::new_full( + config.transaction_pool.clone(), + true.into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let (network, system_rpc_tx, network_starter) = { + let params = BuildNetworkParams { + config: &config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + on_demand: None, + block_announce_validator_builder: None, warp_sync: None, - }; - build_network(params)? - }; - - // offchain workers - sc_service::build_offchain_workers( - &config, - task_manager.spawn_handle(), - client.clone(), - network.clone(), - ); - - // Proposer object for block authorship. - let env = sc_basic_authorship::ProposerFactory::new( - task_manager.spawn_handle(), - client.clone(), - transaction_pool.clone(), - config.prometheus_registry(), - None - ); - - // Channel for the rpc handler to communicate with the authorship task. - let (command_sink, commands_stream) = mpsc::channel(10); - - let rpc_sink = command_sink.clone(); - - let rpc_handlers = { - let params = SpawnTasksParams { - config, - client: client.clone(), - backend: backend.clone(), - task_manager: &mut task_manager, - keystore: keystore.sync_keystore(), - on_demand: None, - transaction_pool: transaction_pool.clone(), - rpc_extensions_builder: Box::new(move |_, _| { - let mut io = jsonrpc_core::IoHandler::default(); - io.extend_with( - ManualSealApi::to_delegate(ManualSeal::new(rpc_sink.clone())) - ); - io - }), - remote_blockchain: None, - network, - system_rpc_tx, - telemetry: None - }; - spawn_tasks(params)? - }; - - let cloned_client = client.clone(); - let create_inherent_data_providers = Box::new(move |_, _| { - let client = cloned_client.clone(); - async move { - let timestamp = SlotTimestampProvider::new(client.clone()).map_err(|err| format!("{:?}", err))?; - let babe = sp_consensus_babe::inherents::InherentDataProvider::new(timestamp.slot().into()); - Ok((timestamp, babe)) - } - }); - - // Background authorship future. - let authorship_future = run_manual_seal(ManualSealParams { - block_import, - env, - client: client.clone(), - pool: transaction_pool.clone(), - commands_stream, - select_chain, - consensus_data_provider: Some(Box::new(consensus_data_provider)), - create_inherent_data_providers, - }); - - // spawn the authorship task as an essential task. - task_manager - .spawn_essential_handle() - .spawn("manual-seal", authorship_future); - - network_starter.start_network(); - let rpc_handler = rpc_handlers.io_handler(); - - Ok(( - rpc_handler, - task_manager, - client, - transaction_pool, - command_sink, - backend, - )) + }; + build_network(params)? + }; + + // offchain workers + sc_service::build_offchain_workers( + &config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + + // Proposer object for block authorship. + let env = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + config.prometheus_registry(), + None + ); + + // Channel for the rpc handler to communicate with the authorship task. + let (command_sink, commands_stream) = mpsc::channel(10); + + let rpc_sink = command_sink.clone(); + + let rpc_handlers = { + let params = SpawnTasksParams { + config, + client: client.clone(), + backend: backend.clone(), + task_manager: &mut task_manager, + keystore: keystore.sync_keystore(), + on_demand: None, + transaction_pool: transaction_pool.clone(), + rpc_extensions_builder: Box::new(move |_, _| { + let mut io = jsonrpc_core::IoHandler::default(); + io.extend_with( + ManualSealApi::to_delegate(ManualSeal::new(rpc_sink.clone())) + ); + io + }), + remote_blockchain: None, + network, + system_rpc_tx, + telemetry: None + }; + spawn_tasks(params)? + }; + + let cloned_client = client.clone(); + let create_inherent_data_providers = Box::new(move |_, _| { + let client = cloned_client.clone(); + async move { + let timestamp = SlotTimestampProvider::new(client.clone()).map_err(|err| format!("{:?}", err))?; + let babe = sp_consensus_babe::inherents::InherentDataProvider::new(timestamp.slot().into()); + Ok((timestamp, babe)) + } + }); + + // Background authorship future. + let authorship_future = run_manual_seal(ManualSealParams { + block_import, + env, + client: client.clone(), + pool: transaction_pool.clone(), + commands_stream, + select_chain, + consensus_data_provider: Some(Box::new(consensus_data_provider)), + create_inherent_data_providers, + }); + + // spawn the authorship task as an essential task. + task_manager + .spawn_essential_handle() + .spawn("manual-seal", authorship_future); + + network_starter.start_network(); + let rpc_handler = rpc_handlers.io_handler(); + + Ok(( + rpc_handler, + task_manager, + client, + transaction_pool, + command_sink, + backend, + )) } From 681452b6185011280b68384c581575f960e58a88 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 17 Jul 2021 13:59:46 +0200 Subject: [PATCH 08/20] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- client/network/src/protocol/sync.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 63b40cf2666e8..54744475c3b6e 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -624,6 +624,7 @@ impl ChainSync { (Some(sync), _) => Some(sync.progress()), _ => None, }; + Status { state: sync_state, best_seen_block: best_seen, @@ -695,6 +696,7 @@ impl ChainSync { } } } + // If we are at genesis, just start downloading. let (state, req) = if self.best_queued_number.is_zero() { debug!( From 2b577bfa8ce35fbe7c05bdfedef8d6a9998711e5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 22 Jul 2021 08:33:34 +0200 Subject: [PATCH 09/20] fmt --- bin/node/cli/src/service.rs | 4 +- client/cli/src/arg_enums.rs | 17 +- client/consensus/aura/src/import_queue.rs | 175 +- client/consensus/babe/src/lib.rs | 695 ++++---- client/consensus/epochs/src/lib.rs | 519 +++--- .../manual-seal/src/consensus/babe.rs | 183 +- client/consensus/manual-seal/src/lib.rs | 367 ++-- client/consensus/pow/src/lib.rs | 198 +-- client/db/src/cache/list_cache.rs | 1485 ++++++++++++----- client/db/src/lib.rs | 1268 +++++++------- client/finality-grandpa/src/environment.rs | 2 +- client/finality-grandpa/src/import.rs | 159 +- client/finality-grandpa/src/lib.rs | 307 ++-- client/finality-grandpa/src/warp_proof.rs | 137 +- client/informant/src/display.rs | 58 +- client/informant/src/lib.rs | 2 +- client/network/src/behaviour.rs | 199 +-- client/network/src/config.rs | 162 +- client/network/src/lib.rs | 21 +- client/network/src/protocol.rs | 725 ++++---- client/network/src/protocol/sync.rs | 1158 ++++++------- client/network/src/protocol/sync/warp.rs | 63 +- client/network/src/service/tests.rs | 3 +- client/network/src/warp_request_handler.rs | 43 +- client/network/test/src/lib.rs | 455 ++--- client/rpc/src/state/tests.rs | 262 ++- client/service/src/builder.rs | 588 ++++--- client/service/src/client/client.rs | 958 ++++++----- .../consensus/common/src/import_queue.rs | 117 +- test-utils/test-runner/src/client.rs | 128 +- 30 files changed, 5516 insertions(+), 4942 deletions(-) diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index 18b108835665e..1f6eb500e1a7e 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -229,8 +229,8 @@ pub fn new_full_base( config.network.extra_sets.push(grandpa::grandpa_peers_set_config()); let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - import_setup.1.shared_authority_set().clone(), + backend.clone(), + import_setup.1.shared_authority_set().clone(), )); let (network, system_rpc_tx, network_starter) = diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index d8c834ce19c50..72741d7bea2bb 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -74,9 +74,8 @@ impl WasmExecutionMethod { impl Into for WasmExecutionMethod { fn into(self) -> sc_service::config::WasmExecutionMethod { match self { - WasmExecutionMethod::Interpreted => { - sc_service::config::WasmExecutionMethod::Interpreted - } + WasmExecutionMethod::Interpreted => + sc_service::config::WasmExecutionMethod::Interpreted, #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => sc_service::config::WasmExecutionMethod::Compiled, #[cfg(not(feature = "wasmtime"))] @@ -252,14 +251,10 @@ impl Into for SyncMode { fn into(self) -> sc_network::config::SyncMode { match self { SyncMode::Full => sc_network::config::SyncMode::Full, - SyncMode::Fast => sc_network::config::SyncMode::Fast { - skip_proofs: false, - storage_chain_mode: false, - }, - SyncMode::FastUnsafe => sc_network::config::SyncMode::Fast { - skip_proofs: true, - storage_chain_mode: false, - }, + SyncMode::Fast => + sc_network::config::SyncMode::Fast { skip_proofs: false, storage_chain_mode: false }, + SyncMode::FastUnsafe => + sc_network::config::SyncMode::Fast { skip_proofs: true, storage_chain_mode: false }, SyncMode::Warp => sc_network::config::SyncMode::Warp, } } diff --git a/client/consensus/aura/src/import_queue.rs b/client/consensus/aura/src/import_queue.rs index 0f5a2ac114d96..4c47a06d512d3 100644 --- a/client/consensus/aura/src/import_queue.rs +++ b/client/consensus/aura/src/import_queue.rs @@ -18,36 +18,35 @@ //! Module implementing the logic for verifying and importing AuRa blocks. -use crate::{AuthorityId, find_pre_digest, slot_author, aura_err, Error, authorities}; -use std::{ - sync::Arc, marker::PhantomData, hash::Hash, fmt::Debug, -}; +use crate::{aura_err, authorities, find_pre_digest, slot_author, AuthorityId, Error}; +use codec::{Codec, Decode, Encode}; use log::{debug, info, trace}; use prometheus_endpoint::Registry; -use codec::{Encode, Decode, Codec}; +use sc_client_api::{backend::AuxStore, BlockOf, UsageProvider}; +use sc_consensus_slots::{check_equivocation, CheckedHeader, InherentDataProviderExt}; +use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE}; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_block_builder::BlockBuilder as BlockBuilderApi; +use sp_blockchain::{ + well_known_cache_keys::{self, Id as CacheKeyId}, + HeaderBackend, ProvideCache, +}; use sp_consensus::{ - BlockImport, CanAuthorWith, ForkChoiceStrategy, BlockImportParams, - Error as ConsensusError, - import_queue::{ - Verifier, BasicQueue, DefaultImportQueue, BoxJustificationImport, - }, + import_queue::{BasicQueue, BoxJustificationImport, DefaultImportQueue, Verifier}, + BlockImport, BlockImportParams, CanAuthorWith, Error as ConsensusError, ForkChoiceStrategy, }; -use sc_client_api::{BlockOf, UsageProvider, backend::AuxStore}; -use sp_blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, ProvideCache, HeaderBackend}; -use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; -use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor}; -use sp_api::ProvideRuntimeApi; +use sp_consensus_aura::{ + digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi, ConsensusLog, + AURA_ENGINE_ID, +}; +use sp_consensus_slots::Slot; use sp_core::crypto::Pair; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _}; -use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_TRACE, CONSENSUS_DEBUG}; -use sc_consensus_slots::{CheckedHeader, check_equivocation, InherentDataProviderExt}; -use sp_consensus_slots::Slot; -use sp_api::ApiExt; -use sp_consensus_aura::{ - digests::CompatibleDigestItem, AuraApi, inherents::AuraInherentData, - ConsensusLog, AURA_ENGINE_ID, +use sp_runtime::{ + generic::{BlockId, OpaqueDigestItemId}, + traits::{Block as BlockT, DigestItemFor, Header}, }; +use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc}; /// check a header has been signed by the right key. If the slot is too far in the future, an error /// will be returned. If it's successful, returns the pre-header and the digest item @@ -61,7 +60,8 @@ fn check_header( hash: B::Hash, authorities: &[AuthorityId

], check_for_equivocation: CheckForEquivocation, -) -> Result)>, Error> where +) -> Result)>, Error> +where DigestItemFor: CompatibleDigestItem, P::Signature: Codec, C: sc_client_api::backend::AuxStore, @@ -69,9 +69,7 @@ fn check_header( { let seal = header.digest_mut().pop().ok_or_else(|| Error::HeaderUnsealed(hash))?; - let sig = seal.as_aura_seal().ok_or_else(|| { - aura_err(Error::HeaderBadSeal(hash)) - })?; + let sig = seal.as_aura_seal().ok_or_else(|| aura_err(Error::HeaderBadSeal(hash)))?; let slot = find_pre_digest::(&header)?; @@ -81,20 +79,17 @@ fn check_header( } else { // check the signature is valid under the expected authority and // chain state. - let expected_author = slot_author::

(slot, &authorities) - .ok_or_else(|| Error::SlotAuthorNotFound)?; + let expected_author = + slot_author::

(slot, &authorities).ok_or_else(|| Error::SlotAuthorNotFound)?; let pre_hash = header.hash(); if P::verify(&sig, pre_hash.as_ref(), expected_author) { if check_for_equivocation.check_for_equivocation() { - if let Some(equivocation_proof) = check_equivocation( - client, - slot_now, - slot, - &header, - expected_author, - ).map_err(Error::Client)? { + if let Some(equivocation_proof) = + check_equivocation(client, slot_now, slot, &header, expected_author) + .map_err(Error::Client)? + { info!( target: "aura", "Slot author is equivocating at slot {} with headers {:?} and {:?}", @@ -141,7 +136,8 @@ impl AuraVerifier { } } -impl AuraVerifier where +impl AuraVerifier +where P: Send + Sync + 'static, CAW: Send + Sync + 'static, CIDP: Send, @@ -152,8 +148,10 @@ impl AuraVerifier where block_id: BlockId, inherent_data: sp_inherents::InherentData, create_inherent_data_providers: CIDP::InherentDataProviders, - ) -> Result<(), Error> where - C: ProvideRuntimeApi, C::Api: BlockBuilderApi, + ) -> Result<(), Error> + where + C: ProvideRuntimeApi, + C::Api: BlockBuilderApi, CAW: CanAuthorWith, CIDP: CreateInherentDataProviders, { @@ -167,11 +165,11 @@ impl AuraVerifier where return Ok(()) } - let inherent_res = self.client.runtime_api().check_inherents( - &block_id, - block, - inherent_data, - ).map_err(|e| Error::Client(e.into()))?; + let inherent_res = self + .client + .runtime_api() + .check_inherents(&block_id, block, inherent_data) + .map_err(|e| Error::Client(e.into()))?; if !inherent_res.ok() { for (i, e) in inherent_res.into_errors() { @@ -187,13 +185,14 @@ impl AuraVerifier where } #[async_trait::async_trait] -impl Verifier for AuraVerifier where - C: ProvideRuntimeApi + - Send + - Sync + - sc_client_api::backend::AuxStore + - ProvideCache + - BlockOf, +impl Verifier for AuraVerifier +where + C: ProvideRuntimeApi + + Send + + Sync + + sc_client_api::backend::AuxStore + + ProvideCache + + BlockOf, C::Api: BlockBuilderApi + AuraApi> + ApiExt, DigestItemFor: CompatibleDigestItem, P: Pair + Send + Sync + 'static, @@ -212,15 +211,14 @@ impl Verifier for AuraVerifier w let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash)) .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; - let create_inherent_data_providers = self.create_inherent_data_providers - .create_inherent_data_providers( - parent_hash, - (), - ) + let create_inherent_data_providers = self + .create_inherent_data_providers + .create_inherent_data_providers(parent_hash, ()) .await .map_err(|e| Error::::Client(sp_blockchain::Error::Application(e)))?; - let mut inherent_data = create_inherent_data_providers.create_inherent_data() + let mut inherent_data = create_inherent_data_providers + .create_inherent_data() .map_err(Error::::Inherent)?; let slot_now = create_inherent_data_providers.slot(); @@ -235,7 +233,8 @@ impl Verifier for AuraVerifier w hash, &authorities[..], self.check_for_equivocation, - ).map_err(|e| e.to_string())?; + ) + .map_err(|e| e.to_string())?; match checked_header { CheckedHeader::Checked(pre_header, (slot, seal)) => { // if the body is passed through, we need to use the runtime @@ -247,7 +246,8 @@ impl Verifier for AuraVerifier w inherent_data.aura_replace_inherent_data(slot); // skip the inherents verification if the runtime API is old. - if self.client + if self + .client .runtime_api() .has_api_with::, _>( &BlockId::Hash(parent_hash), @@ -260,7 +260,9 @@ impl Verifier for AuraVerifier w BlockId::Hash(parent_hash), inherent_data, create_inherent_data_providers, - ).await.map_err(|e| e.to_string())?; + ) + .await + .map_err(|e| e.to_string())?; } let (_, inner_body) = new_block.deconstruct(); @@ -276,16 +278,18 @@ impl Verifier for AuraVerifier w ); // Look for an authorities-change log. - let maybe_keys = pre_header.digest() + let maybe_keys = pre_header + .digest() .logs() .iter() - .filter_map(|l| l.try_to::>>( - OpaqueDigestItemId::Consensus(&AURA_ENGINE_ID) - )) + .filter_map(|l| { + l.try_to::>>(OpaqueDigestItemId::Consensus( + &AURA_ENGINE_ID, + )) + }) .find_map(|l| match l { - ConsensusLog::AuthoritiesChange(a) => Some( - vec![(well_known_cache_keys::AUTHORITIES, a.encode())] - ), + ConsensusLog::AuthoritiesChange(a) => + Some(vec![(well_known_cache_keys::AUTHORITIES, a.encode())]), _ => None, }); @@ -295,7 +299,7 @@ impl Verifier for AuraVerifier w block.post_hash = Some(hash); Ok((block, maybe_keys)) - } + }, CheckedHeader::Deferred(a, b) => { debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); telemetry!( @@ -307,7 +311,7 @@ impl Verifier for AuraVerifier w "b" => ?b, ); Err(format!("Header {:?} rejected: too far in the future", hash)) - } + }, } } } @@ -370,8 +374,9 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>( can_author_with, check_for_equivocation, telemetry, - }: ImportQueueParams<'a, Block, I, C, S, CAW, CIDP> -) -> Result, sp_consensus::Error> where + }: ImportQueueParams<'a, Block, I, C, S, CAW, CIDP>, +) -> Result, sp_consensus::Error> +where Block: BlockT, C::Api: BlockBuilderApi + AuraApi> + ApiExt, C: 'static @@ -383,7 +388,7 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>( + AuxStore + UsageProvider + HeaderBackend, - I: BlockImport> + I: BlockImport> + Send + Sync + 'static, @@ -396,23 +401,15 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>( CIDP: CreateInherentDataProviders + Sync + Send + 'static, CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync, { - let verifier = build_verifier::( - BuildVerifierParams { - client, - create_inherent_data_providers, - can_author_with, - check_for_equivocation, - telemetry, - }, - ); + let verifier = build_verifier::(BuildVerifierParams { + client, + create_inherent_data_providers, + can_author_with, + check_for_equivocation, + telemetry, + }); - Ok(BasicQueue::new( - verifier, - Box::new(block_import), - justification_import, - spawner, - registry, - )) + Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry)) } /// Parameters of [`build_verifier`]. @@ -437,7 +434,7 @@ pub fn build_verifier( can_author_with, check_for_equivocation, telemetry, - }: BuildVerifierParams + }: BuildVerifierParams, ) -> AuraVerifier { AuraVerifier::<_, P, _, _>::new( client, diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 155b640e814c9..0ada3ee4f0f20 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -71,9 +71,13 @@ use std::{ }; use codec::{Decode, Encode}; -use futures::channel::mpsc::{channel, Receiver, Sender}; -use futures::channel::oneshot; -use futures::prelude::*; +use futures::{ + channel::{ + mpsc::{channel, Receiver, Sender}, + oneshot, + }, + prelude::*, +}; use log::{debug, info, log, trace, warn}; use parking_lot::Mutex; use prometheus_endpoint::Registry; @@ -89,18 +93,16 @@ use sc_consensus_slots::{ SlotInfo, StorageChanges, }; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE}; -use sp_api::ApiExt; -use sp_api::{NumberFor, ProvideRuntimeApi}; +use sp_api::{ApiExt, NumberFor, ProvideRuntimeApi}; use sp_application_crypto::AppKey; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ Error as ClientError, HeaderBackend, HeaderMetadata, ProvideCache, Result as ClientResult, }; -use sp_consensus::{import_queue::BoxJustificationImport, CanAuthorWith, ImportResult}; use sp_consensus::{ - import_queue::{BasicQueue, CacheKeyId, DefaultImportQueue, Verifier}, - BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, Environment, - Error as ConsensusError, ForkChoiceStrategy, Proposer, SelectChain, SlotData, + import_queue::{BasicQueue, BoxJustificationImport, CacheKeyId, DefaultImportQueue, Verifier}, + BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, CanAuthorWith, Environment, + Error as ConsensusError, ForkChoiceStrategy, ImportResult, Proposer, SelectChain, SlotData, StateAction, }; use sp_consensus_babe::inherents::BabeInherentData; @@ -158,7 +160,7 @@ impl EpochT for Epoch { fn increment( &self, - (descriptor, config): (NextEpochDescriptor, BabeEpochConfiguration) + (descriptor, config): (NextEpochDescriptor, BabeEpochConfiguration), ) -> Epoch { Epoch { epoch_index: self.epoch_index + 1, @@ -195,10 +197,7 @@ impl From for Epoch { impl Epoch { /// Create the genesis epoch (epoch #0). This is defined to start at the slot of /// the first block, so that has to be provided. - pub fn genesis( - genesis_config: &BabeGenesisConfiguration, - slot: Slot, - ) -> Epoch { + pub fn genesis(genesis_config: &BabeGenesisConfiguration, slot: Slot) -> Epoch { Epoch { epoch_index: 0, start_slot: slot, @@ -265,7 +264,11 @@ pub enum Error { #[display(fmt = "No secondary author expected.")] NoSecondaryAuthorExpected, /// VRF verification of block by author failed - #[display(fmt = "VRF verification of block by author {:?} failed: threshold {} exceeded", _0, _1)] + #[display( + fmt = "VRF verification of block by author {:?} failed: threshold {} exceeded", + _0, + _1 + )] VRFVerificationOfBlockFailed(AuthorityId, u128), /// VRF verification failed #[display(fmt = "VRF verification failed: {:?}", _0)] @@ -332,35 +335,36 @@ pub struct Config(sc_consensus_slots::SlotDuration); impl Config { /// Either fetch the slot duration from disk or compute it from the genesis /// state. - pub fn get_or_compute(client: &C) -> ClientResult where - C: AuxStore + ProvideRuntimeApi + UsageProvider, C::Api: BabeApi, + pub fn get_or_compute(client: &C) -> ClientResult + where + C: AuxStore + ProvideRuntimeApi + UsageProvider, + C::Api: BabeApi, { trace!(target: "babe", "Getting slot duration"); match sc_consensus_slots::SlotDuration::get_or_compute(client, |a, b| { - let has_api_v1 = a.has_api_with::, _>( - &b, |v| v == 1, - )?; - let has_api_v2 = a.has_api_with::, _>( - &b, |v| v == 2, - )?; + let has_api_v1 = a.has_api_with::, _>(&b, |v| v == 1)?; + let has_api_v2 = a.has_api_with::, _>(&b, |v| v == 2)?; if has_api_v1 { - #[allow(deprecated)] { + #[allow(deprecated)] + { Ok(a.configuration_before_version_2(b)?.into()) } } else if has_api_v2 { a.configuration(b).map_err(Into::into) } else { Err(sp_blockchain::Error::VersionInvalid( - "Unsupported or invalid BabeApi version".to_string() + "Unsupported or invalid BabeApi version".to_string(), )) } - }).map(Self) { + }) + .map(Self) + { Ok(s) => Ok(s), Err(s) => { warn!(target: "babe", "Failed to get slot duration"); Err(s) - } + }, } } @@ -514,7 +518,8 @@ where let (worker_tx, worker_rx) = channel(HANDLE_BUFFER_SIZE); - let answer_requests = answer_requests(worker_rx, config.0, client, babe_link.epoch_changes.clone()); + let answer_requests = + answer_requests(worker_rx, config.0, client, babe_link.epoch_changes.clone()); Ok(BabeWorker { inner: Box::pin(future::join(inner, answer_requests).map(|_| ())), slot_notification_sinks, @@ -527,28 +532,37 @@ async fn answer_requests( genesis_config: sc_consensus_slots::SlotDuration, client: Arc, epoch_changes: SharedEpochChanges, -) - where C: ProvideRuntimeApi + ProvideCache + ProvideUncles + BlockchainEvents - + HeaderBackend + HeaderMetadata + Send + Sync + 'static, +) where + C: ProvideRuntimeApi + + ProvideCache + + ProvideUncles + + BlockchainEvents + + HeaderBackend + + HeaderMetadata + + Send + + Sync + + 'static, { while let Some(request) = request_rx.next().await { match request { BabeRequest::EpochForChild(parent_hash, parent_number, slot_number, response) => { let lookup = || { let epoch_changes = epoch_changes.shared_data(); - let epoch_descriptor = epoch_changes.epoch_descriptor_for_child_of( - descendent_query(&*client), - &parent_hash, - parent_number, - slot_number, - ) + let epoch_descriptor = epoch_changes + .epoch_descriptor_for_child_of( + descendent_query(&*client), + &parent_hash, + parent_number, + slot_number, + ) .map_err(|e| Error::::ForkTree(Box::new(e)))? .ok_or_else(|| Error::::FetchEpoch(parent_hash))?; - let viable_epoch = epoch_changes.viable_epoch( - &epoch_descriptor, - |slot| Epoch::genesis(&genesis_config, slot) - ).ok_or_else(|| Error::::FetchEpoch(parent_hash))?; + let viable_epoch = epoch_changes + .viable_epoch(&epoch_descriptor, |slot| { + Epoch::genesis(&genesis_config, slot) + }) + .ok_or_else(|| Error::::FetchEpoch(parent_hash))?; Ok(sp_consensus_babe::Epoch { epoch_index: viable_epoch.as_ref().epoch_index, @@ -561,7 +575,7 @@ async fn answer_requests( }; let _ = response.send(lookup()); - } + }, } } } @@ -596,7 +610,7 @@ impl BabeWorkerHandle { /// Worker for Babe which implements `Future`. This must be polled. #[must_use] pub struct BabeWorker { - inner: Pin + Send + 'static>>, + inner: Pin + Send + 'static>>, slot_notification_sinks: SlotNotificationSinks, handle: BabeWorkerHandle, } @@ -605,7 +619,7 @@ impl BabeWorker { /// Return an event stream of notifications for when new slot happens, and the corresponding /// epoch descriptor. pub fn slot_notification_stream( - &self + &self, ) -> Receiver<(Slot, ViableEpochDescriptor, Epoch>)> { const CHANNEL_BUFFER_SIZE: usize = 1024; @@ -625,7 +639,7 @@ impl futures::Future for BabeWorker { fn poll( mut self: Pin<&mut Self>, - cx: &mut futures::task::Context + cx: &mut futures::task::Context, ) -> futures::task::Poll { self.inner.as_mut().poll(cx) } @@ -633,7 +647,7 @@ impl futures::Future for BabeWorker { /// Slot notification sinks. type SlotNotificationSinks = Arc< - Mutex::Hash, NumberFor, Epoch>)>>> + Mutex::Hash, NumberFor, Epoch>)>>>, >; struct BabeSlotWorker { @@ -674,9 +688,8 @@ where type Claim = (PreDigest, AuthorityId); type SyncOracle = SO; type JustificationSyncLink = L; - type CreateProposer = Pin> + Send + 'static - >>; + type CreateProposer = + Pin> + Send + 'static>>; type Proposer = E::Proposer; type BlockImport = I; @@ -693,12 +706,14 @@ where parent: &B::Header, slot: Slot, ) -> Result { - self.epoch_changes.shared_data().epoch_descriptor_for_child_of( - descendent_query(&*self.client), - &parent.hash(), - parent.number().clone(), - slot, - ) + self.epoch_changes + .shared_data() + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent.hash(), + parent.number().clone(), + slot, + ) .map_err(|e| ConsensusError::ChainLookup(format!("{:?}", e)))? .ok_or(sp_consensus::Error::InvalidAuthoritiesSet) } @@ -719,10 +734,10 @@ where debug!(target: "babe", "Attempting to claim slot {}", slot); let s = authorship::claim_slot( slot, - self.epoch_changes.shared_data().viable_epoch( - &epoch_descriptor, - |slot| Epoch::genesis(&self.config, slot) - )?.as_ref(), + self.epoch_changes + .shared_data() + .viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))? + .as_ref(), &self.keystore, ); @@ -739,20 +754,18 @@ where slot: Slot, epoch_descriptor: &ViableEpochDescriptor, Epoch>, ) { - self.slot_notification_sinks.lock() - .retain_mut(|sink| { - match sink.try_send((slot, epoch_descriptor.clone())) { - Ok(()) => true, - Err(e) => { - if e.is_full() { - warn!(target: "babe", "Trying to notify a slot but the channel is full"); - true - } else { - false - } + self.slot_notification_sinks.lock().retain_mut(|sink| { + match sink.try_send((slot, epoch_descriptor.clone())) { + Ok(()) => true, + Err(e) => + if e.is_full() { + warn!(target: "babe", "Trying to notify a slot but the channel is full"); + true + } else { + false }, - } - }); + } + }); } fn pre_digest_data( @@ -760,59 +773,64 @@ where _slot: Slot, claim: &Self::Claim, ) -> Vec> { - vec![ - as CompatibleDigestItem>::babe_pre_digest(claim.0.clone()), - ] + vec![ as CompatibleDigestItem>::babe_pre_digest(claim.0.clone())] } - fn block_import_params(&self) -> Box, - StorageChanges, - Self::Claim, - Self::EpochData, - ) -> Result< - sp_consensus::BlockImportParams, - sp_consensus::Error> + Send + 'static> - { + fn block_import_params( + &self, + ) -> Box< + dyn Fn( + B::Header, + &B::Hash, + Vec, + StorageChanges, + Self::Claim, + Self::EpochData, + ) -> Result, sp_consensus::Error> + + Send + + 'static, + > { let keystore = self.keystore.clone(); - Box::new(move |header, header_hash, body, storage_changes, (_, public), epoch_descriptor| { - // sign the pre-sealed hash of the block and then - // add it to a digest item. - let public_type_pair = public.clone().into(); - let public = public.to_raw_vec(); - let signature = SyncCryptoStore::sign_with( - &*keystore, - ::ID, - &public_type_pair, - header_hash.as_ref() - ) - .map_err(|e| sp_consensus::Error::CannotSign( - public.clone(), e.to_string(), - ))? - .ok_or_else(|| sp_consensus::Error::CannotSign( - public.clone(), "Could not find key in keystore.".into(), - ))?; - let signature: AuthoritySignature = signature.clone().try_into() - .map_err(|_| sp_consensus::Error::InvalidSignature( - signature, public - ))?; - let digest_item = as CompatibleDigestItem>::babe_seal(signature.into()); - - let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); - import_block.post_digests.push(digest_item); - import_block.body = Some(body); - import_block.state_action = StateAction::ApplyChanges( - sp_consensus::StorageChanges::Changes(storage_changes) - ); - import_block.intermediates.insert( - Cow::from(INTERMEDIATE_KEY), - Box::new(BabeIntermediate:: { epoch_descriptor }) as Box<_>, - ); + Box::new( + move |header, header_hash, body, storage_changes, (_, public), epoch_descriptor| { + // sign the pre-sealed hash of the block and then + // add it to a digest item. + let public_type_pair = public.clone().into(); + let public = public.to_raw_vec(); + let signature = SyncCryptoStore::sign_with( + &*keystore, + ::ID, + &public_type_pair, + header_hash.as_ref(), + ) + .map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))? + .ok_or_else(|| { + sp_consensus::Error::CannotSign( + public.clone(), + "Could not find key in keystore.".into(), + ) + })?; + let signature: AuthoritySignature = signature + .clone() + .try_into() + .map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?; + let digest_item = + as CompatibleDigestItem>::babe_seal(signature.into()); + + let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); + import_block.post_digests.push(digest_item); + import_block.body = Some(body); + import_block.state_action = StateAction::ApplyChanges( + sp_consensus::StorageChanges::Changes(storage_changes), + ); + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate:: { epoch_descriptor }) as Box<_>, + ); - Ok(import_block) - }) + Ok(import_block) + }, + ) } fn force_authoring(&self) -> bool { @@ -821,8 +839,8 @@ where fn should_backoff(&self, slot: Slot, chain_head: &B::Header) -> bool { if let Some(ref strategy) = self.backoff_authoring_blocks { - if let Ok(chain_head_slot) = find_pre_digest::(chain_head) - .map(|digest| digest.slot()) + if let Ok(chain_head_slot) = + find_pre_digest::(chain_head).map(|digest| digest.slot()) { return strategy.should_backoff( *chain_head.number(), @@ -830,7 +848,7 @@ where self.client.info().finalized_number, slot, self.logging_target(), - ); + ) } } false @@ -845,9 +863,11 @@ where } fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer { - Box::pin(self.env.init(block).map_err(|e| { - sp_consensus::Error::ClientImport(format!("{:?}", e)) - })) + Box::pin( + self.env + .init(block) + .map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e))), + ) } fn telemetry(&self) -> Option { @@ -877,7 +897,7 @@ pub fn find_pre_digest(header: &B::Header) -> Result = None; @@ -893,16 +913,19 @@ pub fn find_pre_digest(header: &B::Header) -> Result(header: &B::Header) - -> Result, Error> - where DigestItemFor: CompatibleDigestItem, +fn find_next_epoch_digest( + header: &B::Header, +) -> Result, Error> +where + DigestItemFor: CompatibleDigestItem, { let mut epoch_digest: Option<_> = None; for log in header.digest().logs() { trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log); let log = log.try_to::(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID)); match (log, epoch_digest.is_some()) { - (Some(ConsensusLog::NextEpochData(_)), true) => return Err(babe_err(Error::MultipleEpochChangeDigests)), + (Some(ConsensusLog::NextEpochData(_)), true) => + return Err(babe_err(Error::MultipleEpochChangeDigests)), (Some(ConsensusLog::NextEpochData(epoch)), false) => epoch_digest = Some(epoch), _ => trace!(target: "babe", "Ignoring digest not meant for us"), } @@ -912,16 +935,19 @@ fn find_next_epoch_digest(header: &B::Header) } /// Extract the BABE config change digest from the given header, if it exists. -fn find_next_config_digest(header: &B::Header) - -> Result, Error> - where DigestItemFor: CompatibleDigestItem, +fn find_next_config_digest( + header: &B::Header, +) -> Result, Error> +where + DigestItemFor: CompatibleDigestItem, { let mut config_digest: Option<_> = None; for log in header.digest().logs() { trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log); let log = log.try_to::(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID)); match (log, config_digest.is_some()) { - (Some(ConsensusLog::NextConfigData(_)), true) => return Err(babe_err(Error::MultipleConfigChangeDigests)), + (Some(ConsensusLog::NextConfigData(_)), true) => + return Err(babe_err(Error::MultipleConfigChangeDigests)), (Some(ConsensusLog::NextConfigData(config)), false) => config_digest = Some(config), _ => trace!(target: "babe", "Ignoring digest not meant for us"), } @@ -986,11 +1012,11 @@ where return Ok(()) } - let inherent_res = self.client.runtime_api().check_inherents( - &block_id, - block, - inherent_data, - ).map_err(Error::RuntimeApi)?; + let inherent_res = self + .client + .runtime_api() + .check_inherents(&block_id, block, inherent_data) + .map_err(Error::RuntimeApi)?; if !inherent_res.ok() { for (i, e) in inherent_res.into_errors() { @@ -1015,7 +1041,7 @@ where // don't report any equivocations during initial sync // as they are most likely stale. if *origin == BlockOrigin::NetworkInitialSync { - return Ok(()); + return Ok(()) } // check if authorship of this header is an equivocation and return a proof if so. @@ -1065,8 +1091,8 @@ where Some(proof) => proof, None => { debug!(target: "babe", "Equivocation offender is not part of the authority set."); - return Ok(()); - } + return Ok(()) + }, }, }; @@ -1086,13 +1112,8 @@ where } } -type BlockVerificationResult = Result< - ( - BlockImportParams, - Option)>>, - ), - String, ->; +type BlockVerificationResult = + Result<(BlockImportParams, Option)>>), String>; #[async_trait::async_trait] impl Verifier @@ -1146,24 +1167,26 @@ where let slot_now = create_inherent_data_providers.slot(); - let parent_header_metadata = self.client.header_metadata(parent_hash) + let parent_header_metadata = self + .client + .header_metadata(parent_hash) .map_err(Error::::FetchParentHeader)?; let pre_digest = find_pre_digest::(&block.header)?; let (check_header, epoch_descriptor) = { let epoch_changes = self.epoch_changes.shared_data(); - let epoch_descriptor = epoch_changes.epoch_descriptor_for_child_of( - descendent_query(&*self.client), - &parent_hash, - parent_header_metadata.number, - pre_digest.slot(), - ) - .map_err(|e| Error::::ForkTree(Box::new(e)))? - .ok_or_else(|| Error::::FetchEpoch(parent_hash))?; - let viable_epoch = epoch_changes.viable_epoch( - &epoch_descriptor, - |slot| Epoch::genesis(&self.config, slot) - ).ok_or_else(|| Error::::FetchEpoch(parent_hash))?; + let epoch_descriptor = epoch_changes + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent_hash, + parent_header_metadata.number, + pre_digest.slot(), + ) + .map_err(|e| Error::::ForkTree(Box::new(e)))? + .ok_or_else(|| Error::::FetchEpoch(parent_hash))?; + let viable_epoch = epoch_changes + .viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot)) + .ok_or_else(|| Error::::FetchEpoch(parent_hash))?; // We add one to the current slot to allow for some small drift. // FIXME #1019 in the future, alter this queue to allow deferring of headers @@ -1179,20 +1202,25 @@ where match check_header { CheckedHeader::Checked(pre_header, verified_info) => { - let babe_pre_digest = verified_info.pre_digest.as_babe_pre_digest() + let babe_pre_digest = verified_info + .pre_digest + .as_babe_pre_digest() .expect("check_header always returns a pre-digest digest item; qed"); let slot = babe_pre_digest.slot(); // the header is valid but let's check if there was something else already // proposed at the same slot by the given author. if there was, we will // report the equivocation to the runtime. - if let Err(err) = self.check_and_report_equivocation( - slot_now, - slot, - &block.header, - &verified_info.author, - &block.origin, - ).await { + if let Err(err) = self + .check_and_report_equivocation( + slot_now, + slot, + &block.header, + &verified_info.author, + &block.origin, + ) + .await + { warn!(target: "babe", "Error checking/reporting BABE equivocation: {:?}", err); } @@ -1200,7 +1228,8 @@ where // to check that the internally-set timestamp in the inherents // actually matches the slot set in the seal. if let Some(inner_body) = block.body { - let mut inherent_data = create_inherent_data_providers.create_inherent_data() + let mut inherent_data = create_inherent_data_providers + .create_inherent_data() .map_err(Error::::CreateInherents)?; inherent_data.babe_replace_inherent_data(slot); let new_block = Block::new(pre_header.clone(), inner_body); @@ -1210,7 +1239,8 @@ where BlockId::Hash(parent_hash), inherent_data, create_inherent_data_providers, - ).await?; + ) + .await?; let (_, inner_body) = new_block.deconstruct(); block.body = Some(inner_body); @@ -1233,7 +1263,7 @@ where block.post_hash = Some(hash); Ok((block, Default::default())) - } + }, CheckedHeader::Deferred(a, b) => { debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); telemetry!( @@ -1243,7 +1273,7 @@ where "hash" => ?hash, "a" => ?a, "b" => ?b ); Err(Error::::TooFarInFuture(hash).into()) - } + }, } } } @@ -1281,21 +1311,22 @@ impl BabeBlockImport { block_import: I, config: Config, ) -> Self { - BabeBlockImport { - client, - inner: block_import, - epoch_changes, - config, - } + BabeBlockImport { client, inner: block_import, epoch_changes, config } } } -impl BabeBlockImport where +impl BabeBlockImport +where Block: BlockT, Inner: BlockImport> + Send + Sync, Inner::Error: Into, - Client: HeaderBackend + HeaderMetadata - + AuxStore + ProvideRuntimeApi + ProvideCache + Send + Sync, + Client: HeaderBackend + + HeaderMetadata + + AuxStore + + ProvideRuntimeApi + + ProvideCache + + Send + + Sync, Client::Api: BabeApi + ApiExt, { /// Import whole state after warp sync. @@ -1312,48 +1343,57 @@ impl BabeBlockImport where block.fork_choice = Some(ForkChoiceStrategy::Custom(true)); // Reset block weight. - aux_schema::write_block_weight( - hash, - 0, - |values| block.auxiliary.extend( - values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) - ), - ); + aux_schema::write_block_weight(hash, 0, |values| { + block + .auxiliary + .extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))) + }); // First make the client import the state. let import_result = self.inner.import_block(block, new_cache).await; let aux = match import_result { Ok(ImportResult::Imported(aux)) => aux, Ok(r) => - return Err(ConsensusError::ClientImport(format!("Unexpected import result: {:?}", r))), + return Err(ConsensusError::ClientImport(format!( + "Unexpected import result: {:?}", + r + ))), Err(r) => return Err(r.into()), }; // Read epoch info from the imported state. let block_id = BlockId::hash(hash); - let current_epoch = self.client.runtime_api().current_epoch(&block_id) - .map_err(|e| ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()))?; - let next_epoch = self.client.runtime_api().next_epoch(&block_id) - .map_err(|e| ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()))?; + let current_epoch = self.client.runtime_api().current_epoch(&block_id).map_err(|e| { + ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()) + })?; + let next_epoch = self.client.runtime_api().next_epoch(&block_id).map_err(|e| { + ConsensusError::ClientImport(babe_err::(Error::RuntimeApi(e)).into()) + })?; let mut epoch_changes = self.epoch_changes.shared_data_locked(); epoch_changes.reset(parent_hash, hash, number, current_epoch.into(), next_epoch.into()); - aux_schema::write_epoch_changes::( - &*epoch_changes, - |insert| self.client.insert_aux(insert, []) - ).map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + aux_schema::write_epoch_changes::(&*epoch_changes, |insert| { + self.client.insert_aux(insert, []) + }) + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; Ok(ImportResult::Imported(aux)) } } #[async_trait::async_trait] -impl BlockImport for BabeBlockImport where +impl BlockImport for BabeBlockImport +where Block: BlockT, Inner: BlockImport> + Send + Sync, Inner::Error: Into, - Client: HeaderBackend + HeaderMetadata - + AuxStore + ProvideRuntimeApi + ProvideCache + Send + Sync, + Client: HeaderBackend + + HeaderMetadata + + AuxStore + + ProvideRuntimeApi + + ProvideCache + + Send + + Sync, Client::Api: BabeApi + ApiExt, { type Error = ConsensusError; @@ -1380,34 +1420,40 @@ impl BlockImport for BabeBlockImport return Err(ConsensusError::ClientImport(e.to_string())), } - if matches!(block.state_action, StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_))) { - return self.import_state(block, new_cache).await; + if matches!( + block.state_action, + StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_)) + ) { + return self.import_state(block, new_cache).await } - let pre_digest = find_pre_digest::(&block.header) - .expect("valid babe headers must contain a predigest; \ - header has been already verified; qed"); + let pre_digest = find_pre_digest::(&block.header).expect( + "valid babe headers must contain a predigest; \ + header has been already verified; qed", + ); let slot = pre_digest.slot(); let parent_hash = *block.header.parent_hash(); - let parent_header = self.client.header(BlockId::Hash(parent_hash)) + let parent_header = self + .client + .header(BlockId::Hash(parent_hash)) .map_err(|e| ConsensusError::ChainLookup(e.to_string()))? - .ok_or_else(|| ConsensusError::ChainLookup(babe_err( - Error::::ParentUnavailable(parent_hash, hash) - ).into()))?; - - let parent_slot = find_pre_digest::(&parent_header) - .map(|d| d.slot()) - .expect("parent is non-genesis; valid BABE headers contain a pre-digest; \ - header has already been verified; qed"); + .ok_or_else(|| { + ConsensusError::ChainLookup( + babe_err(Error::::ParentUnavailable(parent_hash, hash)).into(), + ) + })?; + + let parent_slot = find_pre_digest::(&parent_header).map(|d| d.slot()).expect( + "parent is non-genesis; valid BABE headers contain a pre-digest; \ + header has already been verified; qed", + ); // make sure that slot number is strictly increasing if slot <= parent_slot { - return Err( - ConsensusError::ClientImport(babe_err( - Error::::SlotMustIncrease(parent_slot, slot) - ).into()) - ); + return Err(ConsensusError::ClientImport( + babe_err(Error::::SlotMustIncrease(parent_slot, slot)).into(), + )) } // if there's a pending epoch we'll save the previous epoch changes here @@ -1430,14 +1476,16 @@ impl BlockImport for BabeBlockImport::ParentBlockNoAssociatedWeight(hash)).into() - ))? + .ok_or_else(|| { + ConsensusError::ClientImport( + babe_err(Error::::ParentBlockNoAssociatedWeight(hash)) + .into(), + ) + })? }; - let intermediate = block.take_intermediate::>( - INTERMEDIATE_KEY - )?; + let intermediate = + block.take_intermediate::>(INTERMEDIATE_KEY)?; let epoch_descriptor = intermediate.epoch_descriptor; let first_in_epoch = parent_slot < epoch_descriptor.start_slot(); @@ -1455,27 +1503,18 @@ impl BlockImport for BabeBlockImport {}, (false, false, false) => {}, - (false, false, true) => { - return Err( - ConsensusError::ClientImport( - babe_err(Error::::UnexpectedConfigChange).into(), - ) - ) - }, - (true, false, _) => { - return Err( - ConsensusError::ClientImport( - babe_err(Error::::ExpectedEpochChange(hash, slot)).into(), - ) - ) - }, - (false, true, _) => { - return Err( - ConsensusError::ClientImport( - babe_err(Error::::UnexpectedEpochChange).into(), - ) - ) - }, + (false, false, true) => + return Err(ConsensusError::ClientImport( + babe_err(Error::::UnexpectedConfigChange).into(), + )), + (true, false, _) => + return Err(ConsensusError::ClientImport( + babe_err(Error::::ExpectedEpochChange(hash, slot)).into(), + )), + (false, true, _) => + return Err(ConsensusError::ClientImport( + babe_err(Error::::UnexpectedEpochChange).into(), + )), } let info = self.client.info(); @@ -1483,16 +1522,15 @@ impl BlockImport for BabeBlockImport::FetchEpoch(parent_hash).into()) - })?; + let viable_epoch = epoch_changes + .viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot)) + .ok_or_else(|| { + ConsensusError::ClientImport(Error::::FetchEpoch(parent_hash).into()) + })?; - let epoch_config = next_config_digest.map(Into::into).unwrap_or_else( - || viable_epoch.as_ref().config.clone() - ); + let epoch_config = next_config_digest + .map(Into::into) + .unwrap_or_else(|| viable_epoch.as_ref().config.clone()); // restrict info logging during initial sync to avoid spam let log_level = if block.origin == BlockOrigin::NetworkInitialSync { @@ -1526,43 +1564,40 @@ impl BlockImport for BabeBlockImport( - &*epoch_changes, - |insert| block.auxiliary.extend( - insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) - ) - ); + crate::aux_schema::write_epoch_changes::(&*epoch_changes, |insert| { + block + .auxiliary + .extend(insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))) + }); } - aux_schema::write_block_weight( - hash, - total_weight, - |values| block.auxiliary.extend( - values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) - ), - ); + aux_schema::write_block_weight(hash, total_weight, |values| { + block + .auxiliary + .extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))) + }); // The fork choice rule is that we pick the heaviest chain (i.e. // more primary blocks), if there's a tie we go with the longest @@ -1577,9 +1612,11 @@ impl BlockImport for BabeBlockImport last_best_weight { @@ -1620,30 +1657,38 @@ impl BlockImport for BabeBlockImport( client: Arc, epoch_changes: &mut EpochChangesFor, -) -> Result<(), ConsensusError> where +) -> Result<(), ConsensusError> +where Block: BlockT, Client: HeaderBackend + HeaderMetadata, { let info = client.info(); let finalized_slot = { - let finalized_header = client.header(BlockId::Hash(info.finalized_hash)) + let finalized_header = client + .header(BlockId::Hash(info.finalized_hash)) .map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))? - .expect("best finalized hash was given by client; \ - finalized headers must exist in db; qed"); + .expect( + "best finalized hash was given by client; \ + finalized headers must exist in db; qed", + ); find_pre_digest::(&finalized_header) - .expect("finalized header must be valid; \ - valid blocks have a pre-digest; qed") + .expect( + "finalized header must be valid; \ + valid blocks have a pre-digest; qed", + ) .slot() }; - epoch_changes.prune_finalized( - descendent_query(&*client), - &info.finalized_hash, - info.finalized_number, - finalized_slot, - ).map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))?; + epoch_changes + .prune_finalized( + descendent_query(&*client), + &info.finalized_hash, + info.finalized_number, + finalized_slot, + ) + .map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))?; Ok(()) } @@ -1662,25 +1707,14 @@ where Client: AuxStore + HeaderBackend + HeaderMetadata, { let epoch_changes = aux_schema::load_epoch_changes::(&*client, &config)?; - let link = BabeLink { - epoch_changes: epoch_changes.clone(), - config: config.clone(), - }; + let link = BabeLink { epoch_changes: epoch_changes.clone(), config: config.clone() }; // NOTE: this isn't entirely necessary, but since we didn't use to prune the // epoch tree it is useful as a migration, so that nodes prune long trees on // startup rather than waiting until importing the next epoch change block. - prune_finalized( - client.clone(), - &mut epoch_changes.shared_data(), - )?; + prune_finalized(client.clone(), &mut epoch_changes.shared_data())?; - let import = BabeBlockImport::new( - client, - epoch_changes, - wrapped_block_import, - config, - ); + let import = BabeBlockImport::new(client, epoch_changes, wrapped_block_import, config); Ok((import, link)) } @@ -1705,12 +1739,23 @@ pub fn import_queue( registry: Option<&Registry>, can_author_with: CAW, telemetry: Option, -) -> ClientResult> where - Inner: BlockImport> - + Send + Sync + 'static, - Client: ProvideRuntimeApi + ProvideCache + HeaderBackend - + HeaderMetadata + AuxStore - + Send + Sync + 'static, +) -> ClientResult> +where + Inner: BlockImport< + Block, + Error = ConsensusError, + Transaction = sp_api::TransactionFor, + > + Send + + Sync + + 'static, + Client: ProvideRuntimeApi + + ProvideCache + + HeaderBackend + + HeaderMetadata + + AuxStore + + Send + + Sync + + 'static, Client::Api: BlockBuilderApi + BabeApi + ApiExt, SelectChain: sp_consensus::SelectChain + 'static, CAW: CanAuthorWith + Send + Sync + 'static, @@ -1727,11 +1772,5 @@ pub fn import_queue( client, }; - Ok(BasicQueue::new( - verifier, - Box::new(block_import), - justification_import, - spawner, - registry, - )) + Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry)) } diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index 0a4830c7e1cbc..52327dbbf60e6 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -20,12 +20,16 @@ pub mod migration; -use std::{ops::{Add, Sub}, collections::BTreeMap, borrow::{Borrow, BorrowMut}}; -use codec::{Encode, Decode}; +use codec::{Decode, Encode}; use fork_tree::ForkTree; use sc_client_api::utils::is_descendent_of; -use sp_blockchain::{HeaderMetadata, HeaderBackend, Error as ClientError}; +use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero}; +use std::{ + borrow::{Borrow, BorrowMut}, + collections::BTreeMap, + ops::{Add, Sub}, +}; /// A builder for `is_descendent_of` functions. pub trait IsDescendentOfBuilder { @@ -41,8 +45,7 @@ pub trait IsDescendentOfBuilder { /// details aren't yet stored, but its parent is. /// /// The format of `current` when `Some` is `(current, current_parent)`. - fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>) - -> Self::IsDescendentOf; + fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>) -> Self::IsDescendentOf; } /// Produce a descendent query object given the client. @@ -55,16 +58,18 @@ pub fn descendent_query(client: &H) -> HeaderBackendDescendentBuilder< pub struct HeaderBackendDescendentBuilder(H, std::marker::PhantomData); impl<'a, H, Block> IsDescendentOfBuilder - for HeaderBackendDescendentBuilder<&'a H, Block> where - H: HeaderBackend + HeaderMetadata, + for HeaderBackendDescendentBuilder<&'a H, Block> +where + H: HeaderBackend + HeaderMetadata, Block: BlockT, { type Error = ClientError; type IsDescendentOf = Box Result + 'a>; - fn build_is_descendent_of(&self, current: Option<(Block::Hash, Block::Hash)>) - -> Self::IsDescendentOf - { + fn build_is_descendent_of( + &self, + current: Option<(Block::Hash, Block::Hash)>, + ) -> Self::IsDescendentOf { Box::new(is_descendent_of(self.0, current)) } } @@ -90,10 +95,7 @@ pub trait Epoch { impl<'a, E: Epoch> From<&'a E> for EpochHeader { fn from(epoch: &'a E) -> EpochHeader { - Self { - start_slot: epoch.start_slot(), - end_slot: epoch.end_slot(), - } + Self { start_slot: epoch.start_slot(), end_slot: epoch.end_slot() } } } @@ -109,10 +111,7 @@ pub struct EpochHeader { impl Clone for EpochHeader { fn clone(&self) -> Self { - Self { - start_slot: self.start_slot, - end_slot: self.end_slot, - } + Self { start_slot: self.start_slot, end_slot: self.end_slot } } } @@ -149,7 +148,8 @@ pub enum ViableEpoch { Signaled(ERef), } -impl AsRef for ViableEpoch where +impl AsRef for ViableEpoch +where ERef: Borrow, { fn as_ref(&self) -> &E { @@ -160,7 +160,8 @@ impl AsRef for ViableEpoch where } } -impl AsMut for ViableEpoch where +impl AsMut for ViableEpoch +where ERef: BorrowMut, { fn as_mut(&mut self) -> &mut E { @@ -171,7 +172,8 @@ impl AsMut for ViableEpoch where } } -impl ViableEpoch where +impl ViableEpoch +where E: Epoch + Clone, ERef: Borrow, { @@ -187,18 +189,14 @@ impl ViableEpoch where /// Get cloned value for the viable epoch. pub fn into_cloned(self) -> ViableEpoch { match self { - ViableEpoch::UnimportedGenesis(e) => - ViableEpoch::UnimportedGenesis(e), + ViableEpoch::UnimportedGenesis(e) => ViableEpoch::UnimportedGenesis(e), ViableEpoch::Signaled(e) => ViableEpoch::Signaled(e.borrow().clone()), } } /// Increment the epoch, yielding an `IncrementedEpoch` to be imported /// into the fork-tree. - pub fn increment( - &self, - next_descriptor: E::NextEpochDescriptor - ) -> IncrementedEpoch { + pub fn increment(&self, next_descriptor: E::NextEpochDescriptor) -> IncrementedEpoch { let next = self.as_ref().increment(next_descriptor); let to_persist = match *self { ViableEpoch::UnimportedGenesis(ref epoch_0) => @@ -216,7 +214,7 @@ pub enum ViableEpochDescriptor { /// The epoch is an unimported genesis, with given start slot number. UnimportedGenesis(E::Slot), /// The epoch is signaled and has been imported, with given identifier and header. - Signaled(EpochIdentifier, EpochHeader) + Signaled(EpochIdentifier, EpochHeader), } impl ViableEpochDescriptor { @@ -243,8 +241,7 @@ impl<'a, E: Epoch> From<&'a PersistedEpoch> for PersistedEpochHeader { match epoch { PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) => PersistedEpochHeader::Genesis(epoch_0.into(), epoch_1.into()), - PersistedEpoch::Regular(ref epoch_n) => - PersistedEpochHeader::Regular(epoch_n.into()), + PersistedEpoch::Regular(ref epoch_n) => PersistedEpochHeader::Regular(epoch_n.into()), } } } @@ -312,7 +309,8 @@ fn fake_head_hash + AsMut<[u8]> + Clone>(parent_hash: &H) -> H { h } -impl Default for EpochChanges where +impl Default for EpochChanges +where Hash: PartialEq + Ord, Number: Ord, { @@ -321,9 +319,10 @@ impl Default for EpochChanges where } } -impl EpochChanges where +impl EpochChanges +where Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy, - Number: Ord + One + Zero + Add + Sub + Copy, + Number: Ord + One + Zero + Add + Sub + Copy, { /// Create a new epoch change. pub fn new() -> Self { @@ -337,51 +336,38 @@ impl EpochChanges where } /// Map the epoch changes from one storing data to a different one. - pub fn map(self, mut f: F) -> EpochChanges where - B: Epoch, + pub fn map(self, mut f: F) -> EpochChanges + where + B: Epoch, F: FnMut(&Hash, &Number, E) -> B, { EpochChanges { - inner: self.inner.map(&mut |_, _, header| { - match header { - PersistedEpochHeader::Genesis(epoch_0, epoch_1) => { - PersistedEpochHeader::Genesis( - EpochHeader { - start_slot: epoch_0.start_slot, - end_slot: epoch_0.end_slot, - }, - EpochHeader { - start_slot: epoch_1.start_slot, - end_slot: epoch_1.end_slot, - }, - ) - }, - PersistedEpochHeader::Regular(epoch_n) => { - PersistedEpochHeader::Regular( - EpochHeader { - start_slot: epoch_n.start_slot, - end_slot: epoch_n.end_slot, - }, - ) - }, - } + inner: self.inner.map(&mut |_, _, header| match header { + PersistedEpochHeader::Genesis(epoch_0, epoch_1) => PersistedEpochHeader::Genesis( + EpochHeader { start_slot: epoch_0.start_slot, end_slot: epoch_0.end_slot }, + EpochHeader { start_slot: epoch_1.start_slot, end_slot: epoch_1.end_slot }, + ), + PersistedEpochHeader::Regular(epoch_n) => + PersistedEpochHeader::Regular(EpochHeader { + start_slot: epoch_n.start_slot, + end_slot: epoch_n.end_slot, + }), }), - epochs: self.epochs.into_iter().map(|((hash, number), epoch)| { - let bepoch = match epoch { - PersistedEpoch::Genesis(epoch_0, epoch_1) => { - PersistedEpoch::Genesis( + epochs: self + .epochs + .into_iter() + .map(|((hash, number), epoch)| { + let bepoch = match epoch { + PersistedEpoch::Genesis(epoch_0, epoch_1) => PersistedEpoch::Genesis( f(&hash, &number, epoch_0), f(&hash, &number, epoch_1), - ) - }, - PersistedEpoch::Regular(epoch_n) => { - PersistedEpoch::Regular( - f(&hash, &number, epoch_n) - ) - }, - }; - ((hash, number), bepoch) - }).collect(), + ), + PersistedEpoch::Regular(epoch_n) => + PersistedEpoch::Regular(f(&hash, &number, epoch_n)), + }; + ((hash, number), bepoch) + }) + .collect(), } } @@ -395,25 +381,17 @@ impl EpochChanges where number: Number, slot: E::Slot, ) -> Result<(), fork_tree::Error> { - let is_descendent_of = descendent_of_builder - .build_is_descendent_of(None); + let is_descendent_of = descendent_of_builder.build_is_descendent_of(None); let predicate = |epoch: &PersistedEpochHeader| match *epoch { - PersistedEpochHeader::Genesis(_, ref epoch_1) => - slot >= epoch_1.end_slot, - PersistedEpochHeader::Regular(ref epoch_n) => - slot >= epoch_n.end_slot, + PersistedEpochHeader::Genesis(_, ref epoch_1) => slot >= epoch_1.end_slot, + PersistedEpochHeader::Regular(ref epoch_n) => slot >= epoch_n.end_slot, }; // prune any epochs which could not be _live_ as of the children of the // finalized block, i.e. re-root the fork tree to the oldest ancestor of // (hash, number) where epoch.end_slot() >= finalized_slot - let removed = self.inner.prune( - hash, - &number, - &is_descendent_of, - &predicate, - )?; + let removed = self.inner.prune(hash, &number, &is_descendent_of, &predicate)?; for (hash, number, _) in removed { self.epochs.remove(&(hash, number)); @@ -424,18 +402,18 @@ impl EpochChanges where /// Get a reference to an epoch with given identifier. pub fn epoch(&self, id: &EpochIdentifier) -> Option<&E> { - self.epochs.get(&(id.hash, id.number)) - .and_then(|v| { - match v { - PersistedEpoch::Genesis(ref epoch_0, _) - if id.position == EpochIdentifierPosition::Genesis0 => Some(epoch_0), - PersistedEpoch::Genesis(_, ref epoch_1) - if id.position == EpochIdentifierPosition::Genesis1 => Some(epoch_1), - PersistedEpoch::Regular(ref epoch_n) - if id.position == EpochIdentifierPosition::Regular => Some(epoch_n), - _ => None, - } - }) + self.epochs.get(&(id.hash, id.number)).and_then(|v| match v { + PersistedEpoch::Genesis(ref epoch_0, _) + if id.position == EpochIdentifierPosition::Genesis0 => + Some(epoch_0), + PersistedEpoch::Genesis(_, ref epoch_1) + if id.position == EpochIdentifierPosition::Genesis1 => + Some(epoch_1), + PersistedEpoch::Regular(ref epoch_n) + if id.position == EpochIdentifierPosition::Regular => + Some(epoch_n), + _ => None, + }) } /// Get a reference to a viable epoch with given descriptor. @@ -443,33 +421,32 @@ impl EpochChanges where &self, descriptor: &ViableEpochDescriptor, make_genesis: G, - ) -> Option> where - G: FnOnce(E::Slot) -> E + ) -> Option> + where + G: FnOnce(E::Slot) -> E, { match descriptor { - ViableEpochDescriptor::UnimportedGenesis(slot) => { - Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))) - }, - ViableEpochDescriptor::Signaled(identifier, _) => { - self.epoch(&identifier).map(ViableEpoch::Signaled) - }, + ViableEpochDescriptor::UnimportedGenesis(slot) => + Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))), + ViableEpochDescriptor::Signaled(identifier, _) => + self.epoch(&identifier).map(ViableEpoch::Signaled), } } /// Get a mutable reference to an epoch with given identifier. pub fn epoch_mut(&mut self, id: &EpochIdentifier) -> Option<&mut E> { - self.epochs.get_mut(&(id.hash, id.number)) - .and_then(|v| { - match v { - PersistedEpoch::Genesis(ref mut epoch_0, _) - if id.position == EpochIdentifierPosition::Genesis0 => Some(epoch_0), - PersistedEpoch::Genesis(_, ref mut epoch_1) - if id.position == EpochIdentifierPosition::Genesis1 => Some(epoch_1), - PersistedEpoch::Regular(ref mut epoch_n) - if id.position == EpochIdentifierPosition::Regular => Some(epoch_n), - _ => None, - } - }) + self.epochs.get_mut(&(id.hash, id.number)).and_then(|v| match v { + PersistedEpoch::Genesis(ref mut epoch_0, _) + if id.position == EpochIdentifierPosition::Genesis0 => + Some(epoch_0), + PersistedEpoch::Genesis(_, ref mut epoch_1) + if id.position == EpochIdentifierPosition::Genesis1 => + Some(epoch_1), + PersistedEpoch::Regular(ref mut epoch_n) + if id.position == EpochIdentifierPosition::Regular => + Some(epoch_n), + _ => None, + }) } /// Get a mutable reference to a viable epoch with given descriptor. @@ -477,16 +454,15 @@ impl EpochChanges where &mut self, descriptor: &ViableEpochDescriptor, make_genesis: G, - ) -> Option> where - G: FnOnce(E::Slot) -> E + ) -> Option> + where + G: FnOnce(E::Slot) -> E, { match descriptor { - ViableEpochDescriptor::UnimportedGenesis(slot) => { - Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))) - }, - ViableEpochDescriptor::Signaled(identifier, _) => { - self.epoch_mut(&identifier).map(ViableEpoch::Signaled) - }, + ViableEpochDescriptor::UnimportedGenesis(slot) => + Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))), + ViableEpochDescriptor::Signaled(identifier, _) => + self.epoch_mut(&identifier).map(ViableEpoch::Signaled), } } @@ -497,18 +473,15 @@ impl EpochChanges where pub fn epoch_data( &self, descriptor: &ViableEpochDescriptor, - make_genesis: G - ) -> Option where + make_genesis: G, + ) -> Option + where G: FnOnce(E::Slot) -> E, E: Clone, { match descriptor { - ViableEpochDescriptor::UnimportedGenesis(slot) => { - Some(make_genesis(*slot)) - }, - ViableEpochDescriptor::Signaled(identifier, _) => { - self.epoch(&identifier).cloned() - }, + ViableEpochDescriptor::UnimportedGenesis(slot) => Some(make_genesis(*slot)), + ViableEpochDescriptor::Signaled(identifier, _) => self.epoch(&identifier).cloned(), } } @@ -524,7 +497,8 @@ impl EpochChanges where parent_number: Number, slot: E::Slot, make_genesis: G, - ) -> Result, fork_tree::Error> where + ) -> Result, fork_tree::Error> + where G: FnOnce(E::Slot) -> E, E: Clone, { @@ -532,7 +506,7 @@ impl EpochChanges where descendent_of_builder, parent_hash, parent_number, - slot + slot, )?; Ok(descriptor.and_then(|des| self.epoch_data(&des, make_genesis))) @@ -555,8 +529,8 @@ impl EpochChanges where // "descends" from our parent-hash. let fake_head_hash = fake_head_hash(parent_hash); - let is_descendent_of = descendent_of_builder - .build_is_descendent_of(Some((fake_head_hash, *parent_hash))); + let is_descendent_of = + descendent_of_builder.build_is_descendent_of(Some((fake_head_hash, *parent_hash))); if parent_number == Zero::zero() { // need to insert the genesis epoch. @@ -569,37 +543,41 @@ impl EpochChanges where // at epoch_1 -- all we're doing here is figuring out which node // we need. let predicate = |epoch: &PersistedEpochHeader| match *epoch { - PersistedEpochHeader::Genesis(ref epoch_0, _) => - epoch_0.start_slot <= slot, - PersistedEpochHeader::Regular(ref epoch_n) => - epoch_n.start_slot <= slot, + PersistedEpochHeader::Genesis(ref epoch_0, _) => epoch_0.start_slot <= slot, + PersistedEpochHeader::Regular(ref epoch_n) => epoch_n.start_slot <= slot, }; - self.inner.find_node_where( - &fake_head_hash, - &(parent_number + One::one()), - &is_descendent_of, - &predicate, - ) + self.inner + .find_node_where( + &fake_head_hash, + &(parent_number + One::one()), + &is_descendent_of, + &predicate, + ) .map(|n| { - n.map(|node| (match node.data { - // Ok, we found our node. - // and here we figure out which of the internal epochs - // of a genesis node to use based on their start slot. - PersistedEpochHeader::Genesis(ref epoch_0, ref epoch_1) => - if epoch_1.start_slot <= slot { - (EpochIdentifierPosition::Genesis1, epoch_1.clone()) - } else { - (EpochIdentifierPosition::Genesis0, epoch_0.clone()) + n.map(|node| { + ( + match node.data { + // Ok, we found our node. + // and here we figure out which of the internal epochs + // of a genesis node to use based on their start slot. + PersistedEpochHeader::Genesis(ref epoch_0, ref epoch_1) => + if epoch_1.start_slot <= slot { + (EpochIdentifierPosition::Genesis1, epoch_1.clone()) + } else { + (EpochIdentifierPosition::Genesis0, epoch_0.clone()) + }, + PersistedEpochHeader::Regular(ref epoch_n) => + (EpochIdentifierPosition::Regular, epoch_n.clone()), }, - PersistedEpochHeader::Regular(ref epoch_n) => - (EpochIdentifierPosition::Regular, epoch_n.clone()), - }, node)).map(|((position, header), node)| { - ViableEpochDescriptor::Signaled(EpochIdentifier { - position, - hash: node.hash, - number: node.number - }, header) + node, + ) + }) + .map(|((position, header), node)| { + ViableEpochDescriptor::Signaled( + EpochIdentifier { position, hash: node.hash, number: node.number }, + header, + ) }) }) } @@ -617,16 +595,11 @@ impl EpochChanges where parent_hash: Hash, epoch: IncrementedEpoch, ) -> Result<(), fork_tree::Error> { - let is_descendent_of = descendent_of_builder - .build_is_descendent_of(Some((hash, parent_hash))); + let is_descendent_of = + descendent_of_builder.build_is_descendent_of(Some((hash, parent_hash))); let header = PersistedEpochHeader::::from(&epoch.0); - let res = self.inner.import( - hash, - number, - header, - &is_descendent_of, - ); + let res = self.inner.import(hash, number, header, &is_descendent_of); match res { Ok(_) | Err(fork_tree::Error::Duplicate) => { @@ -648,22 +621,16 @@ impl EpochChanges where self.epochs.clear(); let persisted = PersistedEpoch::Regular(current); let header = PersistedEpochHeader::from(&persisted); - let _res = self.inner.import( - parent_hash, - number - One::one(), - header, - &|_, _| Ok(false) as Result>, - ); + let _res = self.inner.import(parent_hash, number - One::one(), header, &|_, _| { + Ok(false) as Result> + }); self.epochs.insert((parent_hash, number - One::one()), persisted); let persisted = PersistedEpoch::Regular(next); let header = PersistedEpochHeader::from(&persisted); - let _res = self.inner.import( - hash, - number, - header, - &|_, _| Ok(true) as Result>, - ); + let _res = self.inner.import(hash, number, header, &|_, _| { + Ok(true) as Result> + }); self.epochs.insert((hash, number), persisted); } } @@ -678,8 +645,7 @@ pub type SharedEpochChanges = #[cfg(test)] mod tests { - use super::*; - use super::Epoch as EpochT; + use super::{Epoch as EpochT, *}; #[derive(Debug, PartialEq)] pub struct TestError; @@ -692,15 +658,14 @@ mod tests { impl std::error::Error for TestError {} - impl<'a, F: 'a , H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder for &'a F - where F: Fn(&H, &H) -> Result + impl<'a, F: 'a, H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder for &'a F + where + F: Fn(&H, &H) -> Result, { type Error = TestError; type IsDescendentOf = Box Result + 'a>; - fn build_is_descendent_of(&self, current: Option<(H, H)>) - -> Self::IsDescendentOf - { + fn build_is_descendent_of(&self, current: Option<(H, H)>) -> Self::IsDescendentOf { let f = *self; Box::new(move |base, head| { let mut head = head; @@ -708,7 +673,7 @@ mod tests { if let Some((ref c_head, ref c_parent)) = current { if head == c_head { if base == c_parent { - return Ok(true); + return Ok(true) } else { head = c_parent; } @@ -734,10 +699,7 @@ mod tests { type Slot = Slot; fn increment(&self, _: ()) -> Self { - Epoch { - start_slot: self.start_slot + self.duration, - duration: self.duration, - } + Epoch { start_slot: self.start_slot + self.duration, duration: self.duration } } fn end_slot(&self) -> Slot { @@ -766,12 +728,10 @@ mod tests { }; let epoch_changes = EpochChanges::<_, _, Epoch>::new(); - let genesis_epoch = epoch_changes.epoch_descriptor_for_child_of( - &is_descendent_of, - b"0", - 0, - 10101, - ).unwrap().unwrap(); + let genesis_epoch = epoch_changes + .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10101) + .unwrap() + .unwrap(); match genesis_epoch { ViableEpochDescriptor::UnimportedGenesis(slot) => { @@ -780,12 +740,10 @@ mod tests { _ => panic!("should be unimported genesis"), }; - let genesis_epoch_2 = epoch_changes.epoch_descriptor_for_child_of( - &is_descendent_of, - b"0", - 0, - 10102, - ).unwrap().unwrap(); + let genesis_epoch_2 = epoch_changes + .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10102) + .unwrap() + .unwrap(); match genesis_epoch_2 { ViableEpochDescriptor::UnimportedGenesis(slot) => { @@ -811,34 +769,23 @@ mod tests { } }; - let make_genesis = |slot| Epoch { - start_slot: slot, - duration: 100, - }; + let make_genesis = |slot| Epoch { start_slot: slot, duration: 100 }; let mut epoch_changes = EpochChanges::<_, _, Epoch>::new(); - let genesis_epoch = epoch_changes.epoch_descriptor_for_child_of( - &is_descendent_of, - b"0", - 0, - 100, - ).unwrap().unwrap(); + let genesis_epoch = epoch_changes + .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100) + .unwrap() + .unwrap(); assert_eq!(genesis_epoch, ViableEpochDescriptor::UnimportedGenesis(100)); - let import_epoch_1 = epoch_changes - .viable_epoch(&genesis_epoch, &make_genesis) - .unwrap() - .increment(()); + let import_epoch_1 = + epoch_changes.viable_epoch(&genesis_epoch, &make_genesis).unwrap().increment(()); let epoch_1 = import_epoch_1.as_ref().clone(); - epoch_changes.import( - &is_descendent_of, - *b"A", - 1, - *b"0", - import_epoch_1, - ).unwrap(); + epoch_changes + .import(&is_descendent_of, *b"A", 1, *b"0", import_epoch_1) + .unwrap(); let genesis_epoch = epoch_changes.epoch_data(&genesis_epoch, &make_genesis).unwrap(); assert!(is_descendent_of(b"0", b"A").unwrap()); @@ -848,13 +795,10 @@ mod tests { { // x is still within the genesis epoch. - let x = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"A", - 1, - end_slot - 1, - &make_genesis, - ).unwrap().unwrap(); + let x = epoch_changes + .epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot - 1, &make_genesis) + .unwrap() + .unwrap(); assert_eq!(x, genesis_epoch); } @@ -862,13 +806,10 @@ mod tests { { // x is now at the next epoch, because the block is now at the // start slot of epoch 1. - let x = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"A", - 1, - end_slot, - &make_genesis, - ).unwrap().unwrap(); + let x = epoch_changes + .epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot, &make_genesis) + .unwrap() + .unwrap(); assert_eq!(x, epoch_1); } @@ -876,13 +817,16 @@ mod tests { { // x is now at the next epoch, because the block is now after // start slot of epoch 1. - let x = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"A", - 1, - epoch_1.end_slot() - 1, - &make_genesis, - ).unwrap().unwrap(); + let x = epoch_changes + .epoch_data_for_child_of( + &is_descendent_of, + b"A", + 1, + epoch_1.end_slot() - 1, + &make_genesis, + ) + .unwrap() + .unwrap(); assert_eq!(x, epoch_1); } @@ -905,90 +849,65 @@ mod tests { let duration = 100; - let make_genesis = |slot| Epoch { - start_slot: slot, - duration, - }; + let make_genesis = |slot| Epoch { start_slot: slot, duration }; let mut epoch_changes = EpochChanges::new(); let next_descriptor = (); // insert genesis epoch for A { - let genesis_epoch_a_descriptor = epoch_changes.epoch_descriptor_for_child_of( - &is_descendent_of, - b"0", - 0, - 100, - ).unwrap().unwrap(); + let genesis_epoch_a_descriptor = epoch_changes + .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100) + .unwrap() + .unwrap(); let incremented_epoch = epoch_changes .viable_epoch(&genesis_epoch_a_descriptor, &make_genesis) .unwrap() .increment(next_descriptor.clone()); - epoch_changes.import( - &is_descendent_of, - *b"A", - 1, - *b"0", - incremented_epoch, - ).unwrap(); + epoch_changes + .import(&is_descendent_of, *b"A", 1, *b"0", incremented_epoch) + .unwrap(); } // insert genesis epoch for X { - let genesis_epoch_x_descriptor = epoch_changes.epoch_descriptor_for_child_of( - &is_descendent_of, - b"0", - 0, - 1000, - ).unwrap().unwrap(); + let genesis_epoch_x_descriptor = epoch_changes + .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1000) + .unwrap() + .unwrap(); let incremented_epoch = epoch_changes .viable_epoch(&genesis_epoch_x_descriptor, &make_genesis) .unwrap() .increment(next_descriptor.clone()); - epoch_changes.import( - &is_descendent_of, - *b"X", - 1, - *b"0", - incremented_epoch, - ).unwrap(); + epoch_changes + .import(&is_descendent_of, *b"X", 1, *b"0", incremented_epoch) + .unwrap(); } // now check that the genesis epochs for our respective block 1s // respect the chain structure. { - let epoch_for_a_child = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"A", - 1, - 101, - &make_genesis, - ).unwrap().unwrap(); + let epoch_for_a_child = epoch_changes + .epoch_data_for_child_of(&is_descendent_of, b"A", 1, 101, &make_genesis) + .unwrap() + .unwrap(); assert_eq!(epoch_for_a_child, make_genesis(100)); - let epoch_for_x_child = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"X", - 1, - 1001, - &make_genesis, - ).unwrap().unwrap(); + let epoch_for_x_child = epoch_changes + .epoch_data_for_child_of(&is_descendent_of, b"X", 1, 1001, &make_genesis) + .unwrap() + .unwrap(); assert_eq!(epoch_for_x_child, make_genesis(1000)); - let epoch_for_x_child_before_genesis = epoch_changes.epoch_data_for_child_of( - &is_descendent_of, - b"X", - 1, - 101, - &make_genesis, - ).unwrap(); + let epoch_for_x_child_before_genesis = epoch_changes + .epoch_data_for_child_of(&is_descendent_of, b"X", 1, 101, &make_genesis) + .unwrap(); // even though there is a genesis epoch at that slot, it's not in // this chain. diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs index f1de573170e6e..4cc36aa81f3f3 100644 --- a/client/consensus/manual-seal/src/consensus/babe.rs +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -21,30 +21,39 @@ use super::ConsensusDataProvider; use crate::Error; use codec::Encode; -use std::{borrow::Cow, sync::{Arc, atomic}, time::SystemTime}; use sc_client_api::{AuxStore, UsageProvider}; use sc_consensus_babe::{ - Config, Epoch, authorship, CompatibleDigestItem, BabeIntermediate, INTERMEDIATE_KEY, - find_pre_digest, + authorship, find_pre_digest, BabeIntermediate, CompatibleDigestItem, Config, Epoch, + INTERMEDIATE_KEY, +}; +use sc_consensus_epochs::{ + descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor, }; -use sc_consensus_epochs::{SharedEpochChanges, descendent_query, ViableEpochDescriptor, EpochHeader}; use sp_keystore::SyncCryptoStorePtr; +use std::{ + borrow::Cow, + sync::{atomic, Arc}, + time::SystemTime, +}; use sp_api::{ProvideRuntimeApi, TransactionFor}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; -use sp_consensus::{BlockImportParams, ForkChoiceStrategy}; -use sp_consensus_slots::Slot; +use sp_consensus::{ + import_queue::{CacheKeyId, Verifier}, + BlockImportParams, ForkChoiceStrategy, +}; use sp_consensus_babe::{ - BabeApi, inherents::BabeInherentData, ConsensusLog, BABE_ENGINE_ID, AuthorityId, - digests::{PreDigest, SecondaryPlainPreDigest, NextEpochDescriptor}, BabeAuthorityWeight, + digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest}, + inherents::BabeInherentData, + AuthorityId, BabeApi, BabeAuthorityWeight, ConsensusLog, BABE_ENGINE_ID, }; +use sp_consensus_slots::Slot; use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier}; use sp_runtime::{ - traits::{DigestItemFor, DigestFor, Block as BlockT, Zero, Header}, - generic::{Digest, BlockId}, + generic::{BlockId, Digest}, + traits::{Block as BlockT, DigestFor, DigestItemFor, Header, Zero}, }; -use sp_timestamp::{InherentType, INHERENT_IDENTIFIER, TimestampInherentData}; -use sp_consensus::import_queue::{Verifier, CacheKeyId}; +use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER}; /// Provides BABE-compatible predigests and BlockImportParams. /// Intended for use with BABE runtimes. @@ -77,32 +86,30 @@ pub struct BabeVerifier { impl BabeVerifier { /// create a nrew verifier pub fn new(epoch_changes: SharedEpochChanges, client: Arc) -> BabeVerifier { - BabeVerifier { - epoch_changes, - client, - } + BabeVerifier { epoch_changes, client } } } /// The verifier for the manual seal engine; instantly finalizes. #[async_trait::async_trait] impl Verifier for BabeVerifier - where - B: BlockT, - C: HeaderBackend + HeaderMetadata +where + B: BlockT, + C: HeaderBackend + HeaderMetadata, { async fn verify( &mut self, mut import_params: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - import_params.finalized = false; import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); let pre_digest = find_pre_digest::(&import_params.header)?; let parent_hash = import_params.header.parent_hash(); - let parent = self.client.header(BlockId::Hash(*parent_hash)) + let parent = self + .client + .header(BlockId::Hash(*parent_hash)) .ok() .flatten() .ok_or_else(|| format!("header for block {} not found", parent_hash))?; @@ -129,14 +136,14 @@ impl Verifier for BabeVerifier } impl BabeConsensusDataProvider - where - B: BlockT, - C: AuxStore - + HeaderBackend - + ProvideRuntimeApi - + HeaderMetadata - + UsageProvider, - C::Api: BabeApi, +where + B: BlockT, + C: AuxStore + + HeaderBackend + + ProvideRuntimeApi + + HeaderMetadata + + UsageProvider, + C::Api: BabeApi, { pub fn new( client: Arc, @@ -150,13 +157,7 @@ impl BabeConsensusDataProvider let config = Config::get_or_compute(&*client)?; - Ok(Self { - config, - client, - keystore, - epoch_changes, - authorities, - }) + Ok(Self { config, client, keystore, epoch_changes, authorities }) } fn epoch(&self, parent: &B::Header, slot: Slot) -> Result { @@ -172,10 +173,7 @@ impl BabeConsensusDataProvider .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; let epoch = epoch_changes - .viable_epoch( - &epoch_descriptor, - |slot| Epoch::genesis(&self.config, slot), - ) + .viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot)) .ok_or_else(|| { log::info!(target: "babe", "create_digest: no viable_epoch :("); sp_consensus::Error::InvalidAuthoritiesSet @@ -186,38 +184,37 @@ impl BabeConsensusDataProvider } impl ConsensusDataProvider for BabeConsensusDataProvider - where - B: BlockT, - C: AuxStore - + HeaderBackend - + HeaderMetadata - + UsageProvider - + ProvideRuntimeApi, - C::Api: BabeApi, +where + B: BlockT, + C: AuxStore + + HeaderBackend + + HeaderMetadata + + UsageProvider + + ProvideRuntimeApi, + C::Api: BabeApi, { type Transaction = TransactionFor; - fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result, Error> { - let slot = inherents.babe_inherent_data()? + fn create_digest( + &self, + parent: &B::Header, + inherents: &InherentData, + ) -> Result, Error> { + let slot = inherents + .babe_inherent_data()? .ok_or_else(|| Error::StringError("No babe inherent data".into()))?; let epoch = self.epoch(parent, slot)?; // this is a dev node environment, we should always be able to claim a slot. - let logs = if let Some((predigest, _)) = authorship::claim_slot( - slot, - &epoch, - &self.keystore, - ) { - vec![ - as CompatibleDigestItem>::babe_pre_digest(predigest), - ] + let logs = if let Some((predigest, _)) = + authorship::claim_slot(slot, &epoch, &self.keystore) + { + vec![ as CompatibleDigestItem>::babe_pre_digest(predigest)] } else { // well we couldn't claim a slot because this is an existing chain and we're not in the authorities. // we need to tell BabeBlockImport that the epoch has changed, and we put ourselves in the authorities. - let predigest = PreDigest::SecondaryPlain(SecondaryPlainPreDigest { - slot, - authority_index: 0_u32, - }); + let predigest = + PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0_u32 }); let mut epoch_changes = self.epoch_changes.shared_data(); let epoch_descriptor = epoch_changes @@ -227,12 +224,15 @@ impl ConsensusDataProvider for BabeConsensusDataProvider parent.number().clone(), slot, ) - .map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))? + .map_err(|e| { + Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)) + })? .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; match epoch_descriptor { ViableEpochDescriptor::Signaled(identifier, _epoch_header) => { - let epoch_mut = epoch_changes.epoch_mut(&identifier) + let epoch_mut = epoch_changes + .epoch_mut(&identifier) .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; // mutate the current epoch @@ -246,15 +246,13 @@ impl ConsensusDataProvider for BabeConsensusDataProvider vec![ DigestItemFor::::PreRuntime(BABE_ENGINE_ID, predigest.encode()), - DigestItemFor::::Consensus(BABE_ENGINE_ID, next_epoch.encode()) + DigestItemFor::::Consensus(BABE_ENGINE_ID, next_epoch.encode()), ] }, ViableEpochDescriptor::UnimportedGenesis(_) => { // since this is the genesis, secondary predigest works for now. - vec![ - DigestItemFor::::PreRuntime(BABE_ENGINE_ID, predigest.encode()), - ] - } + vec![DigestItemFor::::PreRuntime(BABE_ENGINE_ID, predigest.encode())] + }, } }; @@ -265,9 +263,10 @@ impl ConsensusDataProvider for BabeConsensusDataProvider &self, parent: &B::Header, params: &mut BlockImportParams, - inherents: &InherentData + inherents: &InherentData, ) -> Result<(), Error> { - let slot = inherents.babe_inherent_data()? + let slot = inherents + .babe_inherent_data()? .ok_or_else(|| Error::StringError("No babe inherent data".into()))?; let epoch_changes = self.epoch_changes.shared_data(); let mut epoch_descriptor = epoch_changes @@ -284,27 +283,27 @@ impl ConsensusDataProvider for BabeConsensusDataProvider // a quick check to see if we're in the authorities let epoch = self.epoch(parent, slot)?; let (authority, _) = self.authorities.first().expect("authorities is non-emptyp; qed"); - let has_authority = epoch.authorities.iter() - .find(|(id, _)| *id == *authority) - .is_some(); + let has_authority = epoch.authorities.iter().find(|(id, _)| *id == *authority).is_some(); if !has_authority { log::info!(target: "manual-seal", "authority not found"); - let timestamp = inherents.timestamp_inherent_data()? + let timestamp = inherents + .timestamp_inherent_data()? .ok_or_else(|| Error::StringError("No timestamp inherent data".into()))?; let slot = *timestamp / self.config.slot_duration; // manually hard code epoch descriptor epoch_descriptor = match epoch_descriptor { - ViableEpochDescriptor::Signaled(identifier, _header) => { + ViableEpochDescriptor::Signaled(identifier, _header) => ViableEpochDescriptor::Signaled( identifier, EpochHeader { start_slot: slot.into(), end_slot: (slot * self.config.epoch_length).into(), }, - ) - }, - _ => unreachable!("we're not in the authorities, so this isn't the genesis epoch; qed") + ), + _ => unreachable!( + "we're not in the authorities, so this isn't the genesis epoch; qed" + ), }; } @@ -321,16 +320,16 @@ impl ConsensusDataProvider for BabeConsensusDataProvider /// Mocks the timestamp inherent to always produce the timestamp for the next babe slot. pub struct SlotTimestampProvider { time: atomic::AtomicU64, - slot_duration: u64 + slot_duration: u64, } impl SlotTimestampProvider { /// Create a new mocked time stamp provider. pub fn new(client: Arc) -> Result - where - B: BlockT, - C: AuxStore + HeaderBackend + ProvideRuntimeApi + UsageProvider, - C::Api: BabeApi, + where + B: BlockT, + C: AuxStore + HeaderBackend + ProvideRuntimeApi + UsageProvider, + C::Api: BabeApi, { let slot_duration = Config::get_or_compute(&*client)?.slot_duration; let info = client.info(); @@ -350,10 +349,7 @@ impl SlotTimestampProvider { .as_millis() as u64 }; - Ok(Self { - time: atomic::AtomicU64::new(time), - slot_duration, - }) + Ok(Self { time: atomic::AtomicU64::new(time), slot_duration }) } /// Get the current slot number @@ -364,12 +360,13 @@ impl SlotTimestampProvider { #[async_trait::async_trait] impl InherentDataProvider for SlotTimestampProvider { - fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), sp_inherents::Error> { + fn provide_inherent_data( + &self, + inherent_data: &mut InherentData, + ) -> Result<(), sp_inherents::Error> { // we update the time here. - let duration: InherentType = self.time.fetch_add( - self.slot_duration, - atomic::Ordering::SeqCst, - ).into(); + let duration: InherentType = + self.time.fetch_add(self.slot_duration, atomic::Ordering::SeqCst).into(); inherent_data.put_data(INHERENT_IDENTIFIER, &duration)?; Ok(()) } diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 313f83a986c4b..060946b7d3a47 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -20,17 +20,16 @@ //! This is suitable for a testing environment. use futures::prelude::*; +use prometheus_endpoint::Registry; +use sc_client_api::backend::{Backend as ClientBackend, Finalizer}; +use sp_blockchain::HeaderBackend; use sp_consensus::{ - Environment, Proposer, SelectChain, BlockImport, - ForkChoiceStrategy, BlockImportParams, - import_queue::{Verifier, BasicQueue, CacheKeyId, BoxBlockImport}, + import_queue::{BasicQueue, BoxBlockImport, CacheKeyId, Verifier}, + BlockImport, BlockImportParams, Environment, ForkChoiceStrategy, Proposer, SelectChain, }; -use sp_blockchain::HeaderBackend; use sp_inherents::CreateInherentDataProviders; use sp_runtime::{traits::Block as BlockT, ConsensusEngineId}; -use sc_client_api::backend::{Backend as ClientBackend, Finalizer}; -use std::{sync::Arc, marker::PhantomData}; -use prometheus_endpoint::Registry; +use std::{marker::PhantomData, sync::Arc}; mod error; mod finalize_block; @@ -40,14 +39,14 @@ pub mod consensus; pub mod rpc; pub use self::{ - error::Error, consensus::ConsensusDataProvider, + error::Error, finalize_block::{finalize_block, FinalizeBlockParams}, - seal_block::{SealBlockParams, seal_block, MAX_PROPOSAL_DURATION}, - rpc::{EngineCommand, CreatedBlock}, + rpc::{CreatedBlock, EngineCommand}, + seal_block::{seal_block, SealBlockParams, MAX_PROPOSAL_DURATION}, }; -use sp_api::{ProvideRuntimeApi, TransactionFor}; use sc_transaction_pool_api::TransactionPool; +use sp_api::{ProvideRuntimeApi, TransactionFor}; /// The `ConsensusEngineId` of Manual Seal. pub const MANUAL_SEAL_ENGINE_ID: ConsensusEngineId = [b'm', b'a', b'n', b'l']; @@ -73,17 +72,11 @@ pub fn import_queue( spawner: &impl sp_core::traits::SpawnEssentialNamed, registry: Option<&Registry>, ) -> BasicQueue - where - Block: BlockT, - Transaction: Send + Sync + 'static, +where + Block: BlockT, + Transaction: Send + Sync + 'static, { - BasicQueue::new( - ManualSealVerifier, - block_import, - None, - spawner, - registry, - ) + BasicQueue::new(ManualSealVerifier, block_import, None, spawner, registry) } /// Params required to start the instant sealing authorship task. @@ -108,7 +101,8 @@ pub struct ManualSealParams, TP, SC, C pub select_chain: SC, /// Digest provider for inclusion in blocks. - pub consensus_data_provider: Option>>>, + pub consensus_data_provider: + Option>>>, /// Something that can create the inherent data providers. pub create_inherent_data_providers: CIDP, @@ -132,7 +126,8 @@ pub struct InstantSealParams, TP, SC, pub select_chain: SC, /// Digest provider for inclusion in blocks. - pub consensus_data_provider: Option>>>, + pub consensus_data_provider: + Option>>>, /// Something that can create the inherent data providers. pub create_inherent_data_providers: CIDP, @@ -149,58 +144,52 @@ pub async fn run_manual_seal( select_chain, consensus_data_provider, create_inherent_data_providers, - }: ManualSealParams -) - where - B: BlockT + 'static, - BI: BlockImport> - + Send + Sync + 'static, - C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, - CB: ClientBackend + 'static, - E: Environment + 'static, - E::Proposer: Proposer>, - CS: Stream::Hash>> + Unpin + 'static, - SC: SelectChain + 'static, - TransactionFor: 'static, - TP: TransactionPool, - CIDP: CreateInherentDataProviders, + }: ManualSealParams, +) where + B: BlockT + 'static, + BI: BlockImport> + + Send + + Sync + + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, + CB: ClientBackend + 'static, + E: Environment + 'static, + E::Proposer: Proposer>, + CS: Stream::Hash>> + Unpin + 'static, + SC: SelectChain + 'static, + TransactionFor: 'static, + TP: TransactionPool, + CIDP: CreateInherentDataProviders, { while let Some(command) = commands_stream.next().await { match command { - EngineCommand::SealNewBlock { - create_empty, - finalize, - parent_hash, - sender, - } => { - seal_block( - SealBlockParams { - sender, - parent_hash, - finalize, - create_empty, - env: &mut env, - select_chain: &select_chain, - block_import: &mut block_import, - consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p), - pool: pool.clone(), - client: client.clone(), - create_inherent_data_providers: &create_inherent_data_providers, - } - ).await; - } + EngineCommand::SealNewBlock { create_empty, finalize, parent_hash, sender } => { + seal_block(SealBlockParams { + sender, + parent_hash, + finalize, + create_empty, + env: &mut env, + select_chain: &select_chain, + block_import: &mut block_import, + consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p), + pool: pool.clone(), + client: client.clone(), + create_inherent_data_providers: &create_inherent_data_providers, + }) + .await; + }, EngineCommand::FinalizeBlock { hash, sender, justification } => { let justification = justification.map(|j| (MANUAL_SEAL_ENGINE_ID, j)); - finalize_block( - FinalizeBlockParams { - hash, - sender, - justification, - finalizer: client.clone(), - _phantom: PhantomData, - } - ).await - } + finalize_block(FinalizeBlockParams { + hash, + sender, + justification, + finalizer: client.clone(), + _phantom: PhantomData, + }) + .await + }, } } } @@ -217,63 +206,57 @@ pub async fn run_instant_seal( select_chain, consensus_data_provider, create_inherent_data_providers, - }: InstantSealParams -) - where - B: BlockT + 'static, - BI: BlockImport> - + Send + Sync + 'static, - C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, - CB: ClientBackend + 'static, - E: Environment + 'static, - E::Proposer: Proposer>, - SC: SelectChain + 'static, - TransactionFor: 'static, - TP: TransactionPool, - CIDP: CreateInherentDataProviders, + }: InstantSealParams, +) where + B: BlockT + 'static, + BI: BlockImport> + + Send + + Sync + + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, + CB: ClientBackend + 'static, + E: Environment + 'static, + E::Proposer: Proposer>, + SC: SelectChain + 'static, + TransactionFor: 'static, + TP: TransactionPool, + CIDP: CreateInherentDataProviders, { // instant-seal creates blocks as soon as transactions are imported // into the transaction pool. - let commands_stream = pool.import_notification_stream() - .map(|_| { - EngineCommand::SealNewBlock { - create_empty: false, - finalize: false, - parent_hash: None, - sender: None, - } - }); - - run_manual_seal( - ManualSealParams { - block_import, - env, - client, - pool, - commands_stream, - select_chain, - consensus_data_provider, - create_inherent_data_providers, - } - ).await + let commands_stream = pool.import_notification_stream().map(|_| EngineCommand::SealNewBlock { + create_empty: false, + finalize: false, + parent_hash: None, + sender: None, + }); + + run_manual_seal(ManualSealParams { + block_import, + env, + client, + pool, + commands_stream, + select_chain, + consensus_data_provider, + create_inherent_data_providers, + }) + .await } #[cfg(test)] mod tests { use super::*; - use substrate_test_runtime_client::{ - DefaultTestClientBuilderExt, - TestClientBuilderExt, - AccountKeyring::*, - TestClientBuilder, - }; - use sc_transaction_pool::{BasicPool, RevalidationType, Options}; - use substrate_test_runtime_transaction_pool::{TestApi, uxt}; - use sc_transaction_pool_api::{TransactionPool, MaintainedTransactionPool, TransactionSource}; - use sp_runtime::generic::BlockId; - use sp_consensus::ImportedAux; use sc_basic_authorship::ProposerFactory; use sc_client_api::BlockBackend; + use sc_transaction_pool::{BasicPool, Options, RevalidationType}; + use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool, TransactionSource}; + use sp_consensus::ImportedAux; + use sp_runtime::generic::BlockId; + use substrate_test_runtime_client::{ + AccountKeyring::*, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, + }; + use substrate_test_runtime_transaction_pool::{uxt, TestApi}; fn api() -> Arc { Arc::new(TestApi::empty()) @@ -296,40 +279,32 @@ mod tests { spawner.clone(), 0, )); - let env = ProposerFactory::new( - spawner.clone(), - client.clone(), - pool.clone(), - None, - None, - ); + let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); // this test checks that blocks are created as soon as transactions are imported into the pool. let (sender, receiver) = futures::channel::oneshot::channel(); let mut sender = Arc::new(Some(sender)); - let commands_stream = pool.pool().validated_pool().import_notification_stream() - .map(move |_| { + let commands_stream = + pool.pool().validated_pool().import_notification_stream().map(move |_| { // we're only going to submit one tx so this fn will only be called once. - let mut_sender = Arc::get_mut(&mut sender).unwrap(); + let mut_sender = Arc::get_mut(&mut sender).unwrap(); let sender = std::mem::take(mut_sender); EngineCommand::SealNewBlock { create_empty: false, finalize: true, parent_hash: None, - sender + sender, } }); - let future = run_manual_seal( - ManualSealParams { - block_import: client.clone(), - env, - client: client.clone(), - pool: pool.clone(), - commands_stream, - select_chain, - create_inherent_data_providers: |_, _| async { Ok(()) }, - consensus_data_provider: None, - } - ); + let future = run_manual_seal(ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.clone(), + commands_stream, + select_chain, + create_inherent_data_providers: |_, _| async { Ok(()) }, + consensus_data_provider: None, + }); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); // spawn the background authorship task @@ -373,27 +348,19 @@ mod tests { spawner.clone(), 0, )); - let env = ProposerFactory::new( - spawner.clone(), - client.clone(), - pool.clone(), - None, - None, - ); + let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); // this test checks that blocks are created as soon as an engine command is sent over the stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal( - ManualSealParams { - block_import: client.clone(), - env, - client: client.clone(), - pool: pool.clone(), - commands_stream, - select_chain, - consensus_data_provider: None, - create_inherent_data_providers: |_, _| async { Ok(()) }, - } - ); + let future = run_manual_seal(ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers: |_, _| async { Ok(()) }, + }); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); // spawn the background authorship task @@ -409,7 +376,9 @@ mod tests { sender: Some(tx), create_empty: false, finalize: false, - }).await.unwrap(); + }) + .await + .unwrap(); let created_block = rx.await.unwrap().unwrap(); // assert that the background task returns ok @@ -432,8 +401,10 @@ mod tests { sink.send(EngineCommand::FinalizeBlock { sender: Some(tx), hash: header.hash(), - justification: None - }).await.unwrap(); + justification: None, + }) + .await + .unwrap(); // assert that the background task returns ok assert_eq!(rx.await.unwrap().unwrap(), ()); } @@ -454,27 +425,19 @@ mod tests { spawner.clone(), 0, )); - let env = ProposerFactory::new( - spawner.clone(), - client.clone(), - pool.clone(), - None, - None, - ); + let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); // this test checks that blocks are created as soon as an engine command is sent over the stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal( - ManualSealParams { - block_import: client.clone(), - env, - client: client.clone(), - pool: pool.clone(), - commands_stream, - select_chain, - consensus_data_provider: None, - create_inherent_data_providers: |_, _| async { Ok(()) }, - } - ); + let future = run_manual_seal(ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers: |_, _| async { Ok(()) }, + }); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); // spawn the background authorship task @@ -491,7 +454,9 @@ mod tests { sender: Some(tx), create_empty: false, finalize: false, - }).await.unwrap(); + }) + .await + .unwrap(); let created_block = rx.await.unwrap().unwrap(); pool_api.increment_nonce(Alice.into()); @@ -517,31 +482,35 @@ mod tests { pool.maintain(sc_transaction_pool_api::ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None, - }).await; + }) + .await; let (tx1, rx1) = futures::channel::oneshot::channel(); - assert!(sink.send(EngineCommand::SealNewBlock { - parent_hash: Some(created_block.hash), - sender: Some(tx1), - create_empty: false, - finalize: false, - }).await.is_ok()); - assert_matches::assert_matches!( - rx1.await.expect("should be no error receiving"), - Ok(_) - ); + assert!(sink + .send(EngineCommand::SealNewBlock { + parent_hash: Some(created_block.hash), + sender: Some(tx1), + create_empty: false, + finalize: false, + }) + .await + .is_ok()); + assert_matches::assert_matches!(rx1.await.expect("should be no error receiving"), Ok(_)); let block = client.block(&BlockId::Number(2)).unwrap().unwrap().block; pool_api.add_block(block, true); pool_api.increment_nonce(Alice.into()); assert!(pool.submit_one(&BlockId::Number(1), SOURCE, uxt(Bob, 0)).await.is_ok()); let (tx2, rx2) = futures::channel::oneshot::channel(); - assert!(sink.send(EngineCommand::SealNewBlock { - parent_hash: Some(created_block.hash), - sender: Some(tx2), - create_empty: false, - finalize: false, - }).await.is_ok()); + assert!(sink + .send(EngineCommand::SealNewBlock { + parent_hash: Some(created_block.hash), + sender: Some(tx2), + create_empty: false, + finalize: false, + }) + .await + .is_ok()); let imported = rx2.await.unwrap().unwrap(); // assert that fork block is in the db assert!(client.header(&BlockId::Hash(imported.hash)).unwrap().is_some()) diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 11a97e105ef86..08d70826a69ce 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -41,34 +41,36 @@ mod worker; -pub use crate::worker::{MiningWorker, MiningMetadata, MiningBuild}; +pub use crate::worker::{MiningBuild, MiningMetadata, MiningWorker}; -use std::{ - sync::Arc, borrow::Cow, collections::HashMap, marker::PhantomData, - cmp::Ordering, time::Duration, -}; +use codec::{Decode, Encode}; use futures::{Future, StreamExt}; +use log::*; use parking_lot::Mutex; -use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents}; -use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId}; -use sp_block_builder::BlockBuilder as BlockBuilderApi; -use sp_runtime::RuntimeString; -use sp_runtime::generic::{BlockId, Digest, DigestItem}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use prometheus_endpoint::Registry; +use sc_client_api::{ + self, + {backend::AuxStore, BlockOf, BlockchainEvents}, +}; use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder as BlockBuilderApi; +use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache}; +use sp_consensus::{ + import_queue::{BasicQueue, BoxBlockImport, BoxJustificationImport, Verifier}, + BlockCheckParams, BlockImport, BlockImportParams, CanAuthorWith, Environment, + Error as ConsensusError, ForkChoiceStrategy, ImportResult, Proposer, SelectChain, SyncOracle, +}; use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; -use sp_consensus::{ - BlockImportParams, ForkChoiceStrategy, SyncOracle, Environment, Proposer, - SelectChain, Error as ConsensusError, CanAuthorWith, BlockImport, BlockCheckParams, ImportResult, +use sp_runtime::{ + generic::{BlockId, Digest, DigestItem}, + traits::{Block as BlockT, Header as HeaderT}, + RuntimeString, }; -use sp_consensus::import_queue::{ - BoxBlockImport, BasicQueue, Verifier, BoxJustificationImport, +use std::{ + borrow::Cow, cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc, + time::Duration, }; -use codec::{Encode, Decode}; -use prometheus_endpoint::Registry; -use sc_client_api; -use log::*; use crate::worker::UntilImportedOrTimeout; @@ -102,7 +104,7 @@ pub enum Error { CheckInherents(sp_inherents::Error), #[display( fmt = "Checking inherents unknown error for identifier: {:?}", - "String::from_utf8_lossy(_0)", + "String::from_utf8_lossy(_0)" )] CheckInherentsUnknownError(sp_inherents::InherentIdentifier), #[display(fmt = "Multiple pre-runtime digests")] @@ -153,7 +155,8 @@ pub struct PowAux { pub total_difficulty: Difficulty, } -impl PowAux where +impl PowAux +where Difficulty: Decode + Default, { /// Read the auxiliary from client. @@ -193,11 +196,7 @@ pub trait PowAlgorithm { /// breaking algorithms will help to protect against selfish mining. /// /// Returns if the new seal should be considered best block. - fn break_tie( - &self, - _own_seal: &Seal, - _new_seal: &Seal, - ) -> bool { + fn break_tie(&self, _own_seal: &Seal, _new_seal: &Seal) -> bool { false } /// Verify that the difficulty is valid against given seal. @@ -238,7 +237,8 @@ impl Clone } } -impl PowBlockImport where +impl PowBlockImport +where B: BlockT, I: BlockImport> + Send + Sync, I::Error: Into, @@ -289,14 +289,15 @@ impl PowBlockImport(block.post_digests.last(), block.header.hash())?; - let intermediate = block.take_intermediate::>( - INTERMEDIATE_KEY - )?; + let intermediate = + block.take_intermediate::>(INTERMEDIATE_KEY)?; let difficulty = match intermediate.difficulty { Some(difficulty) => difficulty, @@ -401,14 +401,12 @@ where Ordering::Less => false, Ordering::Greater => true, Ordering::Equal => { - let best_inner_seal = fetch_seal::( - best_header.digest().logs.last(), - best_hash, - )?; + let best_inner_seal = + fetch_seal::(best_header.digest().logs.last(), best_hash)?; self.algorithm.break_tie(&best_inner_seal, &inner_seal) }, - } + }, )); } @@ -423,35 +421,33 @@ pub struct PowVerifier { } impl PowVerifier { - pub fn new( - algorithm: Algorithm, - ) -> Self { + pub fn new(algorithm: Algorithm) -> Self { Self { algorithm, _marker: PhantomData } } fn check_header( &self, mut header: B::Header, - ) -> Result<(B::Header, DigestItem), Error> where + ) -> Result<(B::Header, DigestItem), Error> + where Algorithm: PowAlgorithm, { let hash = header.hash(); let (seal, inner_seal) = match header.digest_mut().pop() { - Some(DigestItem::Seal(id, seal)) => { + Some(DigestItem::Seal(id, seal)) => if id == POW_ENGINE_ID { (DigestItem::Seal(id, seal.clone()), seal) } else { return Err(Error::WrongEngine(id)) - } - }, + }, _ => return Err(Error::HeaderUnsealed(hash)), }; let pre_hash = header.hash(); if !self.algorithm.preliminary_verify(&pre_hash, &inner_seal)?.unwrap_or(true) { - return Err(Error::FailedPreliminaryVerify); + return Err(Error::FailedPreliminaryVerify) } Ok((header, seal)) @@ -459,7 +455,8 @@ impl PowVerifier { } #[async_trait::async_trait] -impl Verifier for PowVerifier where +impl Verifier for PowVerifier +where Algorithm: PowAlgorithm + Send + Sync, Algorithm::Difficulty: 'static + Send, { @@ -470,15 +467,12 @@ impl Verifier for PowVerifier where let hash = block.header.hash(); let (checked_header, seal) = self.check_header(block.header)?; - let intermediate = PowIntermediate:: { - difficulty: None, - }; + let intermediate = PowIntermediate:: { difficulty: None }; block.header = checked_header; block.post_digests.push(seal); - block.intermediates.insert( - Cow::from(INTERMEDIATE_KEY), - Box::new(intermediate) as Box<_> - ); + block + .intermediates + .insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_>); block.post_hash = Some(hash); Ok((block, None)) @@ -495,10 +489,8 @@ pub fn import_queue( algorithm: Algorithm, spawner: &impl sp_core::traits::SpawnEssentialNamed, registry: Option<&Registry>, -) -> Result< - PowImportQueue, - sp_consensus::Error -> where +) -> Result, sp_consensus::Error> +where B: BlockT, Transaction: Send + Sync + 'static, Algorithm: PowAlgorithm + Clone + Send + Sync + 'static, @@ -506,13 +498,7 @@ pub fn import_queue( { let verifier = PowVerifier::new(algorithm); - Ok(BasicQueue::new( - verifier, - block_import, - justification_import, - spawner, - registry, - )) + Ok(BasicQueue::new(verifier, block_import, justification_import, spawner, registry)) } /// Start the mining worker for PoW. This function provides the necessary helper functions that can @@ -567,13 +553,13 @@ where let task = async move { loop { if timer.next().await.is_none() { - break; + break } if sync_oracle.is_major_syncing() { debug!(target: "pow", "Skipping proposal due to sync."); worker.lock().on_major_syncing(); - return; + return } let best_header = match select_chain.best_chain().await { @@ -585,8 +571,8 @@ where Select best chain error: {:?}", err ); - return; - } + return + }, }; let best_hash = best_header.hash(); @@ -597,11 +583,11 @@ where Probably a node update is required!", err, ); - return; + return } if worker.lock().best_hash() == Some(best_hash) { - return; + return } // The worker is locked for the duration of the whole proposing period. Within this period, @@ -616,23 +602,25 @@ where Fetch difficulty failed: {:?}", err, ); - return; + return }, }; - let inherent_data_providers = - match create_inherent_data_providers.create_inherent_data_providers(best_hash, ()).await { - Ok(x) => x, - Err(err) => { - warn!( - target: "pow", - "Unable to propose new block for authoring. \ - Creating inherent data providers failed: {:?}", - err, - ); - return; - }, - }; + let inherent_data_providers = match create_inherent_data_providers + .create_inherent_data_providers(best_hash, ()) + .await + { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to propose new block for authoring. \ + Creating inherent data providers failed: {:?}", + err, + ); + return + }, + }; let inherent_data = match inherent_data_providers.create_inherent_data() { Ok(r) => r, @@ -643,7 +631,7 @@ where Creating inherent data failed: {:?}", e, ); - return; + return }, }; @@ -667,12 +655,10 @@ where }, }; - let proposal = match proposer.propose( - inherent_data, - inherent_digest, - build_time.clone(), - None, - ).await { + let proposal = match proposer + .propose(inherent_data, inherent_digest, build_time.clone(), None) + .await + { Ok(x) => x, Err(err) => { warn!( @@ -708,9 +694,8 @@ fn find_pre_digest(header: &B::Header) -> Result>, Err for log in header.digest().logs() { trace!(target: "pow", "Checking log {:?}, looking for pre runtime digest", log); match (log, pre_digest.is_some()) { - (DigestItem::PreRuntime(POW_ENGINE_ID, _), true) => { - return Err(Error::MultiplePreRuntimeDigests) - }, + (DigestItem::PreRuntime(POW_ENGINE_ID, _), true) => + return Err(Error::MultiplePreRuntimeDigests), (DigestItem::PreRuntime(POW_ENGINE_ID, v), false) => { pre_digest = Some(v.clone()); }, @@ -727,13 +712,12 @@ fn fetch_seal( hash: B::Hash, ) -> Result, Error> { match digest { - Some(DigestItem::Seal(id, seal)) => { + Some(DigestItem::Seal(id, seal)) => if id == &POW_ENGINE_ID { Ok(seal.clone()) } else { return Err(Error::::WrongEngine(*id).into()) - } - }, + }, _ => return Err(Error::::HeaderUnsealed(hash).into()), } } diff --git a/client/db/src/cache/list_cache.rs b/client/db/src/cache/list_cache.rs index fbbb5ee7f4b91..1808d431dd056 100644 --- a/client/db/src/cache/list_cache.rs +++ b/client/db/src/cache/list_cache.rs @@ -41,18 +41,18 @@ //! Finalized entry E1 is pruned when block B is finalized so that: //! EntryAt(B.number - prune_depth).points_to(E1) -use std::collections::{BTreeSet, BTreeMap}; +use std::collections::{BTreeMap, BTreeSet}; use log::warn; use sp_blockchain::{Error as ClientError, Result as ClientResult}; -use sp_runtime::traits::{ - Block as BlockT, NumberFor, Zero, Bounded, CheckedSub -}; +use sp_runtime::traits::{Block as BlockT, Bounded, CheckedSub, NumberFor, Zero}; -use crate::cache::{CacheItemT, ComplexBlockId, EntryType}; -use crate::cache::list_entry::{Entry, StorageEntry}; -use crate::cache::list_storage::{Storage, StorageTransaction, Metadata}; +use crate::cache::{ + list_entry::{Entry, StorageEntry}, + list_storage::{Metadata, Storage, StorageTransaction}, + CacheItemT, ComplexBlockId, EntryType, +}; /// Pruning strategy. #[derive(Debug, Clone, Copy)] @@ -132,8 +132,8 @@ impl> ListCache pruning_strategy: PruningStrategy>, best_finalized_block: ComplexBlockId, ) -> ClientResult { - let (best_finalized_entry, unfinalized) = storage.read_meta() - .and_then(|meta| read_forks(&storage, meta))?; + let (best_finalized_entry, unfinalized) = + storage.read_meta().and_then(|meta| read_forks(&storage, meta))?; Ok(ListCache { storage, @@ -167,7 +167,7 @@ impl> ListCache // BUT since we're not guaranteeing to provide correct values for forks // behind the finalized block, check if the block is finalized first if !chain::is_finalized_block(&self.storage, &at, Bounded::max_value())? { - return Err(ClientError::NotInFinalizedChain); + return Err(ClientError::NotInFinalizedChain) } self.best_finalized_entry.as_ref() @@ -184,18 +184,21 @@ impl> ListCache match self.find_unfinalized_fork(&at)? { Some(fork) => Some(&fork.head), None => match self.best_finalized_entry.as_ref() { - Some(best_finalized_entry) if chain::is_connected_to_block( - &self.storage, - &at, - &best_finalized_entry.valid_from, - )? => Some(best_finalized_entry), + Some(best_finalized_entry) + if chain::is_connected_to_block( + &self.storage, + &at, + &best_finalized_entry.valid_from, + )? => + Some(best_finalized_entry), _ => None, }, } }; match head { - Some(head) => head.search_best_before(&self.storage, at.number) + Some(head) => head + .search_best_before(&self.storage, at.number) .map(|e| e.map(|e| (e.0.valid_from, e.1, e.0.value))), None => Ok(None), } @@ -213,7 +216,8 @@ impl> ListCache entry_type: EntryType, operations: &mut CommitOperations, ) -> ClientResult<()> { - Ok(operations.append(self.do_on_block_insert(tx, parent, block, value, entry_type, operations)?)) + Ok(operations + .append(self.do_on_block_insert(tx, parent, block, value, entry_type, operations)?)) } /// When previously inserted block is finalized. @@ -242,25 +246,25 @@ impl> ListCache for op in ops.operations { match op { CommitOperation::AppendNewBlock(index, best_block) => { - let mut fork = self.unfinalized.get_mut(index) - .expect("ListCache is a crate-private type; + let mut fork = self.unfinalized.get_mut(index).expect( + "ListCache is a crate-private type; internal clients of ListCache are committing transaction while cache is locked; - CommitOperation holds valid references while cache is locked; qed"); + CommitOperation holds valid references while cache is locked; qed", + ); fork.best_block = Some(best_block); }, CommitOperation::AppendNewEntry(index, entry) => { - let mut fork = self.unfinalized.get_mut(index) - .expect("ListCache is a crate-private type; + let mut fork = self.unfinalized.get_mut(index).expect( + "ListCache is a crate-private type; internal clients of ListCache are committing transaction while cache is locked; - CommitOperation holds valid references while cache is locked; qed"); + CommitOperation holds valid references while cache is locked; qed", + ); fork.best_block = Some(entry.valid_from.clone()); fork.head = entry; }, CommitOperation::AddNewFork(entry) => { - self.unfinalized.push(Fork { - best_block: Some(entry.valid_from.clone()), - head: entry, - }); + self.unfinalized + .push(Fork { best_block: Some(entry.valid_from.clone()), head: entry }); }, CommitOperation::BlockFinalized(block, finalizing_entry, forks) => { self.best_finalized_block = block; @@ -275,7 +279,9 @@ impl> ListCache for (fork_index, updated_fork) in forks.into_iter().rev() { match updated_fork { Some(updated_fork) => self.unfinalized[fork_index] = updated_fork, - None => { self.unfinalized.remove(fork_index); }, + None => { + self.unfinalized.remove(fork_index); + }, } } }, @@ -296,18 +302,18 @@ impl> ListCache let prev_operation = operations.operations.last(); debug_assert!( entry_type != EntryType::Final || - self.unfinalized.is_empty() || - self.best_finalized_block.hash == parent.hash || - match prev_operation { - Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) - => best_finalized_block.hash == parent.hash, - _ => false, - } + self.unfinalized.is_empty() || + self.best_finalized_block.hash == parent.hash || + match prev_operation { + Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) => + best_finalized_block.hash == parent.hash, + _ => false, + } ); // we do not store any values behind finalized if block.number != Zero::zero() && self.best_finalized_block.number >= block.number { - return Ok(None); + return Ok(None) } // if the block is not final, it is possibly appended to/forking from existing unfinalized fork @@ -317,14 +323,14 @@ impl> ListCache // when value hasn't changed and block isn't final, there's nothing we need to do if value.is_none() { - return Ok(None); + return Ok(None) } // first: try to find fork that is known to has the best block we're appending to for (index, fork) in self.unfinalized.iter().enumerate() { if fork.try_append(&parent) { fork_and_action = Some((index, ForkAppendResult::Append)); - break; + break } } @@ -332,11 +338,14 @@ impl> ListCache // - we're appending to the fork for the first time after restart; // - we're forking existing unfinalized fork from the middle; if fork_and_action.is_none() { - let best_finalized_entry_block = self.best_finalized_entry.as_ref().map(|f| f.valid_from.number); + let best_finalized_entry_block = + self.best_finalized_entry.as_ref().map(|f| f.valid_from.number); for (index, fork) in self.unfinalized.iter().enumerate() { - if let Some(action) = fork.try_append_or_fork(&self.storage, &parent, best_finalized_entry_block)? { + if let Some(action) = + fork.try_append_or_fork(&self.storage, &parent, best_finalized_entry_block)? + { fork_and_action = Some((index, action)); - break; + break } } } @@ -351,9 +360,14 @@ impl> ListCache }; tx.insert_storage_entry(&block, &new_storage_entry); - let operation = CommitOperation::AppendNewEntry(index, new_storage_entry.into_entry(block)); - tx.update_meta(self.best_finalized_entry.as_ref(), &self.unfinalized, &operation); - return Ok(Some(operation)); + let operation = + CommitOperation::AppendNewEntry(index, new_storage_entry.into_entry(block)); + tx.update_meta( + self.best_finalized_entry.as_ref(), + &self.unfinalized, + &operation, + ); + return Ok(Some(operation)) }, // fork from the middle of unfinalized fork Some((_, ForkAppendResult::Fork(prev_valid_from))) => { @@ -364,9 +378,14 @@ impl> ListCache }; tx.insert_storage_entry(&block, &new_storage_entry); - let operation = CommitOperation::AddNewFork(new_storage_entry.into_entry(block)); - tx.update_meta(self.best_finalized_entry.as_ref(), &self.unfinalized, &operation); - return Ok(Some(operation)); + let operation = + CommitOperation::AddNewFork(new_storage_entry.into_entry(block)); + tx.update_meta( + self.best_finalized_entry.as_ref(), + &self.unfinalized, + &operation, + ); + return Ok(Some(operation)) }, None => (), } @@ -390,12 +409,17 @@ impl> ListCache return Ok(match new_storage_entry { Some(new_storage_entry) => { tx.insert_storage_entry(&block, &new_storage_entry); - let operation = CommitOperation::AddNewFork(new_storage_entry.into_entry(block)); - tx.update_meta(self.best_finalized_entry.as_ref(), &self.unfinalized, &operation); + let operation = + CommitOperation::AddNewFork(new_storage_entry.into_entry(block)); + tx.update_meta( + self.best_finalized_entry.as_ref(), + &self.unfinalized, + &operation, + ); Some(operation) }, None => None, - }); + }) } // cleanup database from abandoned unfinalized forks and obsolete finalized entries @@ -405,7 +429,11 @@ impl> ListCache match new_storage_entry { Some(new_storage_entry) => { tx.insert_storage_entry(&block, &new_storage_entry); - let operation = CommitOperation::BlockFinalized(block.clone(), Some(new_storage_entry.into_entry(block)), abandoned_forks); + let operation = CommitOperation::BlockFinalized( + block.clone(), + Some(new_storage_entry.into_entry(block)), + abandoned_forks, + ); tx.update_meta(self.best_finalized_entry.as_ref(), &self.unfinalized, &operation); Ok(Some(operation)) }, @@ -424,16 +452,16 @@ impl> ListCache let prev_operation = operations.operations.last(); debug_assert!( self.best_finalized_block.hash == parent.hash || - match prev_operation { - Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) - => best_finalized_block.hash == parent.hash, - _ => false, - } + match prev_operation { + Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) => + best_finalized_block.hash == parent.hash, + _ => false, + } ); // there could be at most one entry that is finalizing - let finalizing_entry = self.storage.read_entry(&block)? - .map(|entry| entry.into_entry(block.clone())); + let finalizing_entry = + self.storage.read_entry(&block)?.map(|entry| entry.into_entry(block.clone())); // cleanup database from abandoned unfinalized forks and obsolete finalized entries let abandoned_forks = self.destroy_abandoned_forks(tx, &block, prev_operation); @@ -458,12 +486,13 @@ impl> ListCache for (index, fork) in self.unfinalized.iter().enumerate() { // we only need to truncate fork if its head is ancestor of truncated block if fork.head.valid_from.number < reverted_block.number { - continue; + continue } // we only need to truncate fork if its head is connected to truncated block - if !chain::is_connected_to_block(&self.storage, reverted_block, &fork.head.valid_from)? { - continue; + if !chain::is_connected_to_block(&self.storage, reverted_block, &fork.head.valid_from)? + { + continue } let updated_fork = fork.truncate( @@ -486,7 +515,7 @@ impl> ListCache fn prune_finalized_entries>( &self, tx: &mut Tx, - block: &ComplexBlockId + block: &ComplexBlockId, ) { let prune_depth = match self.pruning_strategy { PruningStrategy::ByDepth(prune_depth) => prune_depth, @@ -516,18 +545,13 @@ impl> ListCache }; // truncate ancient entry - tx.insert_storage_entry(&ancient_block, &StorageEntry { - prev_valid_from: None, - value: current_entry.value, - }); + tx.insert_storage_entry( + &ancient_block, + &StorageEntry { prev_valid_from: None, value: current_entry.value }, + ); // destroy 'fork' ending with previous entry - destroy_fork( - first_entry_to_truncate, - &self.storage, - tx, - None, - ) + destroy_fork(first_entry_to_truncate, &self.storage, tx, None) }; if let Err(error) = do_pruning() { @@ -544,16 +568,17 @@ impl> ListCache ) -> BTreeSet { // if some block has been finalized already => take it into account let prev_abandoned_forks = match prev_operation { - Some(&CommitOperation::BlockFinalized(_, _, ref abandoned_forks)) => Some(abandoned_forks), + Some(&CommitOperation::BlockFinalized(_, _, ref abandoned_forks)) => + Some(abandoned_forks), _ => None, }; let mut destroyed = prev_abandoned_forks.cloned().unwrap_or_else(|| BTreeSet::new()); - let live_unfinalized = self.unfinalized.iter() - .enumerate() - .filter(|(idx, _)| prev_abandoned_forks + let live_unfinalized = self.unfinalized.iter().enumerate().filter(|(idx, _)| { + prev_abandoned_forks .map(|prev_abandoned_forks| !prev_abandoned_forks.contains(idx)) - .unwrap_or(true)); + .unwrap_or(true) + }); for (index, fork) in live_unfinalized { if fork.head.valid_from.number == block.number { destroyed.insert(index); @@ -575,7 +600,7 @@ impl> ListCache ) -> ClientResult>> { for unfinalized in &self.unfinalized { if unfinalized.matches(&self.storage, block)? { - return Ok(Some(&unfinalized)); + return Ok(Some(&unfinalized)) } } @@ -598,7 +623,8 @@ impl Fork { let range = self.head.search_best_range_before(storage, block.number)?; match range { None => Ok(false), - Some((begin, end)) => chain::is_connected_to_range(storage, block, (&begin, end.as_ref())), + Some((begin, end)) => + chain::is_connected_to_range(storage, block, (&begin, end.as_ref())), } } @@ -629,19 +655,19 @@ impl Fork { // check if the parent is connected to the beginning of the range if !chain::is_connected_to_block(storage, parent, &begin)? { - return Ok(None); + return Ok(None) } // the block is connected to the begin-entry. If begin is the head entry // => we need to append new block to the fork if begin == self.head.valid_from { - return Ok(Some(ForkAppendResult::Append)); + return Ok(Some(ForkAppendResult::Append)) } // the parent block belongs to this fork AND it is located after last finalized entry // => we need to make a new fork if best_finalized_entry_block.map(|f| begin.number > f).unwrap_or(true) { - return Ok(Some(ForkAppendResult::Fork(begin))); + return Ok(Some(ForkAppendResult::Fork(begin))) } Ok(None) @@ -654,12 +680,7 @@ impl Fork { tx: &mut Tx, best_finalized_block: Option>, ) -> ClientResult<()> { - destroy_fork( - self.head.valid_from.clone(), - storage, - tx, - best_finalized_block, - ) + destroy_fork(self.head.valid_from.clone(), storage, tx, best_finalized_block) } /// Truncate fork by deleting all entries that are descendants of given block. @@ -675,18 +696,15 @@ impl Fork { // read pointer to previous entry let entry = storage.require_entry(¤t)?; - // truncation stops when we have reached the ancestor of truncated block + // truncation stops when we have reached the ancestor of truncated block if current.number < reverting_block { // if we have reached finalized block => destroy fork if chain::is_finalized_block(storage, ¤t, best_finalized_block)? { - return Ok(None); + return Ok(None) } // else fork needs to be updated - return Ok(Some(Fork { - best_block: None, - head: entry.into_entry(current), - })); + return Ok(Some(Fork { best_block: None, head: entry.into_entry(current) })) } tx.remove_storage_entry(¤t); @@ -708,7 +726,9 @@ impl Default for CommitOperations { // This should never be allowed for non-test code to avoid revealing its internals. #[cfg(test)] -impl From>> for CommitOperations { +impl From>> + for CommitOperations +{ fn from(operations: Vec>) -> Self { CommitOperations { operations } } @@ -726,30 +746,36 @@ impl CommitOperations { Some(last_operation) => last_operation, None => { self.operations.push(new_operation); - return; + return }, }; // we are able (and obliged to) to merge two consequent block finalization operations match last_operation { - CommitOperation::BlockFinalized(old_finalized_block, old_finalized_entry, old_abandoned_forks) => { - match new_operation { - CommitOperation::BlockFinalized(new_finalized_block, new_finalized_entry, new_abandoned_forks) => { - self.operations.push(CommitOperation::BlockFinalized( - new_finalized_block, - new_finalized_entry, - new_abandoned_forks, - )); - }, - _ => { - self.operations.push(CommitOperation::BlockFinalized( - old_finalized_block, - old_finalized_entry, - old_abandoned_forks, - )); - self.operations.push(new_operation); - }, - } + CommitOperation::BlockFinalized( + old_finalized_block, + old_finalized_entry, + old_abandoned_forks, + ) => match new_operation { + CommitOperation::BlockFinalized( + new_finalized_block, + new_finalized_entry, + new_abandoned_forks, + ) => { + self.operations.push(CommitOperation::BlockFinalized( + new_finalized_block, + new_finalized_entry, + new_abandoned_forks, + )); + }, + _ => { + self.operations.push(CommitOperation::BlockFinalized( + old_finalized_block, + old_finalized_entry, + old_abandoned_forks, + )); + self.operations.push(new_operation); + }, }, _ => { self.operations.push(last_operation); @@ -760,7 +786,12 @@ impl CommitOperations { } /// Destroy fork by deleting all unfinalized entries. -pub fn destroy_fork, Tx: StorageTransaction>( +pub fn destroy_fork< + Block: BlockT, + T: CacheItemT, + S: Storage, + Tx: StorageTransaction, +>( head_valid_from: ComplexBlockId, storage: &S, tx: &mut Tx, @@ -771,7 +802,7 @@ pub fn destroy_fork, Tx: Stor // optionally: deletion stops when we found entry at finalized block if let Some(best_finalized_block) = best_finalized_block { if chain::is_finalized_block(storage, ¤t, best_finalized_block)? { - return Ok(()); + return Ok(()) } } @@ -789,8 +820,8 @@ pub fn destroy_fork, Tx: Stor /// Blockchain related functions. mod chain { - use sp_runtime::traits::Header as HeaderT; use super::*; + use sp_runtime::traits::Header as HeaderT; /// Is the block1 connected both ends of the range. pub fn is_connected_to_range>( @@ -799,8 +830,8 @@ mod chain { range: (&ComplexBlockId, Option<&ComplexBlockId>), ) -> ClientResult { let (begin, end) = range; - Ok(is_connected_to_block(storage, block, begin)? - && match end { + Ok(is_connected_to_block(storage, block, begin)? && + match end { Some(end) => is_connected_to_block(storage, block, end)?, None => true, }) @@ -813,10 +844,12 @@ mod chain { block2: &ComplexBlockId, ) -> ClientResult { let (begin, end) = if *block1 > *block2 { (block2, block1) } else { (block1, block2) }; - let mut current = storage.read_header(&end.hash)? + let mut current = storage + .read_header(&end.hash)? .ok_or_else(|| ClientError::UnknownBlock(format!("{}", end.hash)))?; while *current.number() > begin.number { - current = storage.read_header(current.parent_hash())? + current = storage + .read_header(current.parent_hash())? .ok_or_else(|| ClientError::UnknownBlock(format!("{}", current.parent_hash())))?; } @@ -830,11 +863,10 @@ mod chain { best_finalized_block: NumberFor, ) -> ClientResult { if block.number > best_finalized_block { - return Ok(false); + return Ok(false) } - storage.read_id(block.number) - .map(|hash| hash.as_ref() == Some(&block.hash)) + storage.read_id(block.number).map(|hash| hash.as_ref() == Some(&block.hash)) } } @@ -844,17 +876,19 @@ fn read_forks>( meta: Metadata, ) -> ClientResult<(Option>, Vec>)> { let finalized = match meta.finalized { - Some(finalized) => Some(storage.require_entry(&finalized)? - .into_entry(finalized)), + Some(finalized) => Some(storage.require_entry(&finalized)?.into_entry(finalized)), None => None, }; - let unfinalized = meta.unfinalized.into_iter() - .map(|unfinalized| storage.require_entry(&unfinalized) - .map(|storage_entry| Fork { + let unfinalized = meta + .unfinalized + .into_iter() + .map(|unfinalized| { + storage.require_entry(&unfinalized).map(|storage_entry| Fork { best_block: None, head: storage_entry.into_entry(unfinalized), - })) + }) + }) .collect::>()?; Ok((finalized, unfinalized)) @@ -862,10 +896,10 @@ fn read_forks>( #[cfg(test)] mod tests { - use substrate_test_runtime_client::runtime::H256; - use sp_runtime::testing::{Header, Block as RawBlock, ExtrinsicWrapper}; - use crate::cache::list_storage::tests::{DummyStorage, FaultyStorage, DummyTransaction}; use super::*; + use crate::cache::list_storage::tests::{DummyStorage, DummyTransaction, FaultyStorage}; + use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper, Header}; + use substrate_test_runtime_client::runtime::H256; type Block = RawBlock>; @@ -883,7 +917,11 @@ mod tests { fn test_header(number: u64) -> Header { Header { - parent_hash: if number == 0 { Default::default() } else { test_header(number - 1).hash() }, + parent_hash: if number == 0 { + Default::default() + } else { + test_header(number - 1).hash() + }, number, state_root: Default::default(), extrinsics_root: Default::default(), @@ -910,28 +948,54 @@ mod tests { // when block is earlier than best finalized block AND it is not finalized // --- 50 --- // ----------> [100] - assert!(ListCache::<_, u64, _>::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), test_id(100)) - .unwrap().value_at_block(&test_id(50)).is_err()); + assert!(ListCache::<_, u64, _>::new( + DummyStorage::new(), + PruningStrategy::ByDepth(1024), + test_id(100) + ) + .unwrap() + .value_at_block(&test_id(50)) + .is_err()); // when block is earlier than best finalized block AND it is finalized AND value is some // [30] ---- 50 ---> [100] - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(test_id(100)), Vec::new()) - .with_id(50, H256::from_low_u64_be(50)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }) - .with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }), - PruningStrategy::ByDepth(1024), test_id(100) - ).unwrap().value_at_block(&test_id(50)).unwrap(), Some((test_id(30), Some(test_id(100)), 30))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(test_id(100)), Vec::new()) + .with_id(50, H256::from_low_u64_be(50)) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 } + ) + .with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }), + PruningStrategy::ByDepth(1024), + test_id(100) + ) + .unwrap() + .value_at_block(&test_id(50)) + .unwrap(), + Some((test_id(30), Some(test_id(100)), 30)) + ); // when block is the best finalized block AND value is some // ---> [100] - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(test_id(100)), Vec::new()) - .with_id(100, H256::from_low_u64_be(100)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }) - .with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }), - PruningStrategy::ByDepth(1024), test_id(100) - ).unwrap().value_at_block(&test_id(100)).unwrap(), Some((test_id(100), None, 100))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(test_id(100)), Vec::new()) + .with_id(100, H256::from_low_u64_be(100)) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 } + ) + .with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }), + PruningStrategy::ByDepth(1024), + test_id(100) + ) + .unwrap() + .value_at_block(&test_id(100)) + .unwrap(), + Some((test_id(100), None, 100)) + ); // when block is parallel to the best finalized block // ---- 100 // ---> [100] @@ -939,81 +1003,138 @@ mod tests { DummyStorage::new() .with_meta(Some(test_id(100)), Vec::new()) .with_id(50, H256::from_low_u64_be(50)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 } + ) .with_entry(test_id(30), StorageEntry { prev_valid_from: None, value: 30 }), - PruningStrategy::ByDepth(1024), test_id(100) - ).unwrap().value_at_block(&ComplexBlockId::new(H256::from_low_u64_be(2), 100)).is_err()); + PruningStrategy::ByDepth(1024), + test_id(100) + ) + .unwrap() + .value_at_block(&ComplexBlockId::new(H256::from_low_u64_be(2), 100)) + .is_err()); // when block is later than last finalized block AND there are no forks AND finalized value is Some // ---> [100] --- 200 - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(test_id(100)), Vec::new()) - .with_id(50, H256::from_low_u64_be(50)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 }), - PruningStrategy::ByDepth(1024), test_id(100) - ).unwrap().value_at_block(&test_id(200)).unwrap(), Some((test_id(100), None, 100))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(test_id(100)), Vec::new()) + .with_id(50, H256::from_low_u64_be(50)) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(30)), value: 100 } + ), + PruningStrategy::ByDepth(1024), + test_id(100) + ) + .unwrap() + .value_at_block(&test_id(200)) + .unwrap(), + Some((test_id(100), None, 100)) + ); // when block is later than last finalized block AND there are no matching forks // AND block is connected to finalized block AND finalized value is Some // --- 3 // ---> [2] /---------> [4] - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(correct_id(2)), vec![correct_id(4)]) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }) - .with_header(test_header(2)) - .with_header(test_header(3)) - .with_header(test_header(4)) - .with_header(fork_header(0, 2, 3)), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap().value_at_block(&fork_id(0, 2, 3)).unwrap(), Some((correct_id(2), None, 2))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(correct_id(2)), vec![correct_id(4)]) + .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 } + ) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(fork_header(0, 2, 3)), + PruningStrategy::ByDepth(1024), + test_id(2) + ) + .unwrap() + .value_at_block(&fork_id(0, 2, 3)) + .unwrap(), + Some((correct_id(2), None, 2)) + ); // when block is later than last finalized block AND there are no matching forks // AND block is not connected to finalized block // --- 2 --- 3 // 1 /---> [2] ---------> [4] - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(correct_id(2)), vec![correct_id(4)]) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }) - .with_header(test_header(1)) - .with_header(test_header(2)) - .with_header(test_header(3)) - .with_header(test_header(4)) - .with_header(fork_header(0, 1, 3)) - .with_header(fork_header(0, 1, 2)), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap().value_at_block(&fork_id(0, 1, 3)).unwrap(), None); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(correct_id(2)), vec![correct_id(4)]) + .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 } + ) + .with_header(test_header(1)) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(fork_header(0, 1, 3)) + .with_header(fork_header(0, 1, 2)), + PruningStrategy::ByDepth(1024), + test_id(2) + ) + .unwrap() + .value_at_block(&fork_id(0, 1, 3)) + .unwrap(), + None + ); // when block is later than last finalized block AND it appends to unfinalized fork from the end // AND unfinalized value is Some // ---> [2] ---> [4] ---> 5 - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(correct_id(2)), vec![correct_id(4)]) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }) - .with_header(test_header(4)) - .with_header(test_header(5)), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap().value_at_block(&correct_id(5)).unwrap(), Some((correct_id(4), None, 4))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(correct_id(2)), vec![correct_id(4)]) + .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 } + ) + .with_header(test_header(4)) + .with_header(test_header(5)), + PruningStrategy::ByDepth(1024), + test_id(2) + ) + .unwrap() + .value_at_block(&correct_id(5)) + .unwrap(), + Some((correct_id(4), None, 4)) + ); // when block is later than last finalized block AND it does not fits unfinalized fork // AND it is connected to the finalized block AND finalized value is Some // ---> [2] ----------> [4] // \--- 3 - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(Some(correct_id(2)), vec![correct_id(4)]) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_header(test_header(2)) - .with_header(test_header(3)) - .with_header(test_header(4)) - .with_header(fork_header(0, 2, 3)), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap().value_at_block(&fork_id(0, 2, 3)).unwrap(), Some((correct_id(2), None, 2))); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(Some(correct_id(2)), vec![correct_id(4)]) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 } + ) + .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(fork_header(0, 2, 3)), + PruningStrategy::ByDepth(1024), + test_id(2) + ) + .unwrap() + .value_at_block(&fork_id(0, 2, 3)) + .unwrap(), + Some((correct_id(2), None, 2)) + ); } #[test] @@ -1023,7 +1144,8 @@ mod tests { // when trying to insert block < finalized number let mut ops = Default::default(); - assert!(ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), test_id(100)).unwrap() + assert!(ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), test_id(100)) + .unwrap() .do_on_block_insert( &mut DummyTransaction::new(), test_id(49), @@ -1031,9 +1153,12 @@ mod tests { Some(50), nfin, &mut ops, - ).unwrap().is_none()); + ) + .unwrap() + .is_none()); // when trying to insert block @ finalized number - assert!(ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), test_id(100)).unwrap() + assert!(ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), test_id(100)) + .unwrap() .do_on_block_insert( &mut DummyTransaction::new(), test_id(99), @@ -1041,7 +1166,9 @@ mod tests { Some(100), nfin, &Default::default(), - ).unwrap().is_none()); + ) + .unwrap() + .is_none()); // when trying to insert non-final block AND it appends to the best block of unfinalized fork // AND new value is the same as in the fork' best block @@ -1049,12 +1176,23 @@ mod tests { DummyStorage::new() .with_meta(None, vec![test_id(4)]) .with_entry(test_id(4), StorageEntry { prev_valid_from: None, value: 4 }), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + test_id(2), + ) + .unwrap(); cache.unfinalized[0].best_block = Some(test_id(4)); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, test_id(4), test_id(5), Some(4), nfin, &Default::default()).unwrap(), + cache + .do_on_block_insert( + &mut tx, + test_id(4), + test_id(5), + Some(4), + nfin, + &Default::default() + ) + .unwrap(), Some(CommitOperation::AppendNewBlock(0, test_id(5))), ); assert!(tx.inserted_entries().is_empty()); @@ -1064,12 +1202,24 @@ mod tests { // AND new value is the same as in the fork' best block let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, test_id(4), test_id(5), Some(5), nfin, &Default::default()).unwrap(), + cache + .do_on_block_insert( + &mut tx, + test_id(4), + test_id(5), + Some(5), + nfin, + &Default::default() + ) + .unwrap(), Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: test_id(5), value: 5 })), ); assert_eq!(*tx.inserted_entries(), vec![test_id(5).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: None, unfinalized: vec![test_id(5)] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: None, unfinalized: vec![test_id(5)] }) + ); // when trying to insert non-final block AND it is the first block that appends to the best block of unfinalized fork // AND new value is the same as in the fork' best block @@ -1078,18 +1228,22 @@ mod tests { .with_meta(None, vec![correct_id(4)]) .with_entry(correct_id(4), StorageEntry { prev_valid_from: None, value: 4 }) .with_header(test_header(4)), - PruningStrategy::ByDepth(1024), test_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + test_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert( - &mut tx, - correct_id(4), - correct_id(5), - Some(4), - nfin, - &Default::default(), - ).unwrap(), + cache + .do_on_block_insert( + &mut tx, + correct_id(4), + correct_id(5), + Some(4), + nfin, + &Default::default(), + ) + .unwrap(), Some(CommitOperation::AppendNewBlock(0, correct_id(5))), ); assert!(tx.inserted_entries().is_empty()); @@ -1099,40 +1253,64 @@ mod tests { // AND new value is the same as in the fork' best block let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert( - &mut tx, - correct_id(4), - correct_id(5), - Some(5), - nfin, - &Default::default(), - ).unwrap(), + cache + .do_on_block_insert( + &mut tx, + correct_id(4), + correct_id(5), + Some(5), + nfin, + &Default::default(), + ) + .unwrap(), Some(CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(5), value: 5 })), ); assert_eq!(*tx.inserted_entries(), vec![correct_id(5).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: None, unfinalized: vec![correct_id(5)] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: None, unfinalized: vec![correct_id(5)] }) + ); // when trying to insert non-final block AND it forks unfinalized fork let cache = ListCache::new( DummyStorage::new() .with_meta(Some(correct_id(2)), vec![correct_id(4)]) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 4 }, + ) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) .with_header(test_header(2)) .with_header(test_header(3)) .with_header(test_header(4)), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(3), fork_id(0, 3, 4), Some(14), nfin, &Default::default()) + cache + .do_on_block_insert( + &mut tx, + correct_id(3), + fork_id(0, 3, 4), + Some(14), + nfin, + &Default::default() + ) .unwrap(), Some(CommitOperation::AddNewFork(Entry { valid_from: fork_id(0, 3, 4), value: 14 })), ); assert_eq!(*tx.inserted_entries(), vec![fork_id(0, 3, 4).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(2)), unfinalized: vec![correct_id(4), fork_id(0, 3, 4)] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { + finalized: Some(correct_id(2)), + unfinalized: vec![correct_id(4), fork_id(0, 3, 4)] + }) + ); // when trying to insert non-final block AND there are no unfinalized forks // AND value is the same as last finalized @@ -1140,11 +1318,21 @@ mod tests { DummyStorage::new() .with_meta(Some(correct_id(2)), vec![]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(2), nfin, &Default::default()) + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(2), + nfin, + &Default::default() + ) .unwrap(), None, ); @@ -1157,23 +1345,46 @@ mod tests { DummyStorage::new() .with_meta(Some(correct_id(2)), vec![]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), nfin, &Default::default()) + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(3), + nfin, + &Default::default() + ) .unwrap(), Some(CommitOperation::AddNewFork(Entry { valid_from: correct_id(3), value: 3 })), ); assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(2)), unfinalized: vec![correct_id(3)] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: Some(correct_id(2)), unfinalized: vec![correct_id(3)] }) + ); // when inserting finalized entry AND there are no previous finalized entries - let cache = ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), correct_id(2)).unwrap(); + let cache = + ListCache::new(DummyStorage::new(), PruningStrategy::ByDepth(1024), correct_id(2)) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin, &Default::default()) + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(3), + fin, + &Default::default() + ) .unwrap(), Some(CommitOperation::BlockFinalized( correct_id(3), @@ -1183,17 +1394,31 @@ mod tests { ); assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] }) + ); // when inserting finalized entry AND value is the same as in previous finalized let cache = ListCache::new( DummyStorage::new() .with_meta(Some(correct_id(2)), vec![]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(2), fin, &Default::default()).unwrap(), + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(2), + fin, + &Default::default() + ) + .unwrap(), Some(CommitOperation::BlockFinalized(correct_id(3), None, Default::default())), ); assert!(tx.inserted_entries().is_empty()); @@ -1202,7 +1427,16 @@ mod tests { // when inserting finalized entry AND value differs from previous finalized let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(3), fin, &Default::default()).unwrap(), + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(3), + fin, + &Default::default() + ) + .unwrap(), Some(CommitOperation::BlockFinalized( correct_id(3), Some(Entry { valid_from: correct_id(3), value: 3 }), @@ -1211,7 +1445,10 @@ mod tests { ); assert_eq!(*tx.inserted_entries(), vec![correct_id(3).hash].into_iter().collect()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: Some(correct_id(3)), unfinalized: vec![] }) + ); // inserting finalized entry removes abandoned fork EVEN if new entry is not inserted let cache = ListCache::new( @@ -1219,12 +1456,27 @@ mod tests { .with_meta(Some(correct_id(2)), vec![fork_id(0, 1, 3)]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) .with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: 13 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_insert(&mut tx, correct_id(2), correct_id(3), Some(2), fin, &Default::default()).unwrap(), - Some(CommitOperation::BlockFinalized(correct_id(3), None, vec![0].into_iter().collect())), + cache + .do_on_block_insert( + &mut tx, + correct_id(2), + correct_id(3), + Some(2), + fin, + &Default::default() + ) + .unwrap(), + Some(CommitOperation::BlockFinalized( + correct_id(3), + None, + vec![0].into_iter().collect() + )), ); } @@ -1235,12 +1487,19 @@ mod tests { DummyStorage::new() .with_meta(Some(correct_id(2)), vec![correct_id(5)]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }, + ), + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_finalize(&mut tx, correct_id(2), correct_id(3), &Default::default()).unwrap(), + cache + .do_on_block_finalize(&mut tx, correct_id(2), correct_id(3), &Default::default()) + .unwrap(), Some(CommitOperation::BlockFinalized(correct_id(3), None, Default::default())), ); assert!(tx.inserted_entries().is_empty()); @@ -1254,12 +1513,19 @@ mod tests { DummyStorage::new() .with_meta(Some(correct_id(2)), vec![correct_id(5)]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }), - PruningStrategy::ByDepth(1024), correct_id(4) - ).unwrap(); + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }, + ), + PruningStrategy::ByDepth(1024), + correct_id(4), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_finalize(&mut tx, correct_id(4), correct_id(5), &Default::default()).unwrap(), + cache + .do_on_block_finalize(&mut tx, correct_id(4), correct_id(5), &Default::default()) + .unwrap(), Some(CommitOperation::BlockFinalized( correct_id(5), Some(Entry { valid_from: correct_id(5), value: 5 }), @@ -1268,19 +1534,30 @@ mod tests { ); assert!(tx.inserted_entries().is_empty()); assert!(tx.removed_entries().is_empty()); - assert_eq!(*tx.updated_meta(), Some(Metadata { finalized: Some(correct_id(5)), unfinalized: vec![] })); + assert_eq!( + *tx.updated_meta(), + Some(Metadata { finalized: Some(correct_id(5)), unfinalized: vec![] }) + ); // finalization removes abandoned forks let cache = ListCache::new( DummyStorage::new() .with_meta(Some(correct_id(2)), vec![fork_id(0, 1, 3)]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) .with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: None, value: 13 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); let mut tx = DummyTransaction::new(); assert_eq!( - cache.do_on_block_finalize(&mut tx, correct_id(2), correct_id(3), &Default::default()).unwrap(), - Some(CommitOperation::BlockFinalized(correct_id(3), None, vec![0].into_iter().collect())), + cache + .do_on_block_finalize(&mut tx, correct_id(2), correct_id(3), &Default::default()) + .unwrap(), + Some(CommitOperation::BlockFinalized( + correct_id(3), + None, + vec![0].into_iter().collect() + )), ); } @@ -1290,34 +1567,50 @@ mod tests { DummyStorage::new() .with_meta(Some(correct_id(2)), vec![correct_id(5), correct_id(6)]) .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }) - .with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(5)), value: 6 }), - PruningStrategy::ByDepth(1024), correct_id(2) - ).unwrap(); + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }, + ) + .with_entry( + correct_id(6), + StorageEntry { prev_valid_from: Some(correct_id(5)), value: 6 }, + ), + PruningStrategy::ByDepth(1024), + correct_id(2), + ) + .unwrap(); // when new block is appended to unfinalized fork cache.on_transaction_commit(vec![CommitOperation::AppendNewBlock(0, correct_id(6))].into()); assert_eq!(cache.unfinalized[0].best_block, Some(correct_id(6))); // when new entry is appended to unfinalized fork - cache.on_transaction_commit(vec![ - CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(7), value: 7 }), - ].into()); + cache.on_transaction_commit( + vec![CommitOperation::AppendNewEntry(0, Entry { valid_from: correct_id(7), value: 7 })] + .into(), + ); assert_eq!(cache.unfinalized[0].best_block, Some(correct_id(7))); assert_eq!(cache.unfinalized[0].head, Entry { valid_from: correct_id(7), value: 7 }); // when new fork is added - cache.on_transaction_commit(vec![ - CommitOperation::AddNewFork(Entry { valid_from: correct_id(10), value: 10 }), - ].into()); + cache.on_transaction_commit( + vec![CommitOperation::AddNewFork(Entry { valid_from: correct_id(10), value: 10 })] + .into(), + ); assert_eq!(cache.unfinalized[2].best_block, Some(correct_id(10))); assert_eq!(cache.unfinalized[2].head, Entry { valid_from: correct_id(10), value: 10 }); // when block is finalized + entry is finalized + unfinalized forks are deleted - cache.on_transaction_commit(vec![CommitOperation::BlockFinalized( - correct_id(20), - Some(Entry { valid_from: correct_id(20), value: 20 }), - vec![0, 1, 2].into_iter().collect(), - )].into()); + cache.on_transaction_commit( + vec![CommitOperation::BlockFinalized( + correct_id(20), + Some(Entry { valid_from: correct_id(20), value: 20 }), + vec![0, 1, 2].into_iter().collect(), + )] + .into(), + ); assert_eq!(cache.best_finalized_block, correct_id(20)); - assert_eq!(cache.best_finalized_entry, Some(Entry { valid_from: correct_id(20), value: 20 })); + assert_eq!( + cache.best_finalized_entry, + Some(Entry { valid_from: correct_id(20), value: 20 }) + ); assert!(cache.unfinalized.is_empty()); } @@ -1325,45 +1618,88 @@ mod tests { fn list_find_unfinalized_fork_works() { // ----------> [3] // --- [2] ---------> 4 ---> [5] - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(None, vec![fork_id(0, 1, 3), correct_id(5)]) - .with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) - .with_header(test_header(2)) - .with_header(test_header(3)) - .with_header(test_header(4)) - .with_header(test_header(5)), - PruningStrategy::ByDepth(1024), correct_id(0) - ).unwrap().find_unfinalized_fork((&correct_id(4)).into()).unwrap().unwrap().head.valid_from, correct_id(5)); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(None, vec![fork_id(0, 1, 3), correct_id(5)]) + .with_entry( + fork_id(0, 1, 3), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 } + ) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 } + ) + .with_entry(correct_id(2), StorageEntry { prev_valid_from: None, value: 2 }) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(test_header(5)), + PruningStrategy::ByDepth(1024), + correct_id(0) + ) + .unwrap() + .find_unfinalized_fork((&correct_id(4)).into()) + .unwrap() + .unwrap() + .head + .valid_from, + correct_id(5) + ); // --- [2] ---------------> [5] // ----------> [3] ---> 4 - assert_eq!(ListCache::new( - DummyStorage::new() - .with_meta(None, vec![correct_id(5), fork_id(0, 1, 3)]) - .with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 }) - .with_header(test_header(2)) - .with_header(test_header(3)) - .with_header(test_header(4)) - .with_header(test_header(5)) - .with_header(fork_header(0, 1, 2)) - .with_header(fork_header(0, 1, 3)) - .with_header(fork_header(0, 1, 4)), - PruningStrategy::ByDepth(1024), correct_id(0) - ).unwrap() - .find_unfinalized_fork((&fork_id(0, 1, 4)).into()).unwrap().unwrap().head.valid_from, fork_id(0, 1, 3)); + assert_eq!( + ListCache::new( + DummyStorage::new() + .with_meta(None, vec![correct_id(5), fork_id(0, 1, 3)]) + .with_entry( + fork_id(0, 1, 3), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 } + ) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 } + ) + .with_entry( + correct_id(2), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 } + ) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(test_header(5)) + .with_header(fork_header(0, 1, 2)) + .with_header(fork_header(0, 1, 3)) + .with_header(fork_header(0, 1, 4)), + PruningStrategy::ByDepth(1024), + correct_id(0) + ) + .unwrap() + .find_unfinalized_fork((&fork_id(0, 1, 4)).into()) + .unwrap() + .unwrap() + .head + .valid_from, + fork_id(0, 1, 3) + ); // --- [2] ---------------> [5] // ----------> [3] // -----------------> 4 assert!(ListCache::new( DummyStorage::new() .with_meta(None, vec![correct_id(5), fork_id(0, 1, 3)]) - .with_entry(fork_id(0, 1, 3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 }) - .with_entry(correct_id(2), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 }) + .with_entry( + fork_id(0, 1, 3), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 13 } + ) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(2)), value: 5 } + ) + .with_entry( + correct_id(2), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 2 } + ) .with_header(test_header(2)) .with_header(test_header(3)) .with_header(test_header(4)) @@ -1373,89 +1709,167 @@ mod tests { .with_header(fork_header(1, 1, 2)) .with_header(fork_header(1, 1, 3)) .with_header(fork_header(1, 1, 4)), - PruningStrategy::ByDepth(1024), correct_id(0) - ).unwrap().find_unfinalized_fork((&fork_id(1, 1, 4)).into()).unwrap().is_none()); + PruningStrategy::ByDepth(1024), + correct_id(0) + ) + .unwrap() + .find_unfinalized_fork((&fork_id(1, 1, 4)).into()) + .unwrap() + .is_none()); } #[test] fn fork_matches_works() { // when block is not within list range let storage = DummyStorage::new() - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }, + ) .with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .matches(&storage, (&test_id(20)).into()).unwrap(), false); + assert_eq!( + Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } + .matches(&storage, (&test_id(20)).into()) + .unwrap(), + false + ); // when block is not connected to the begin block let storage = DummyStorage::new() - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)) .with_header(fork_header(0, 2, 4)) .with_header(fork_header(0, 2, 3)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .matches(&storage, (&fork_id(0, 2, 4)).into()).unwrap(), false); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .matches(&storage, (&fork_id(0, 2, 4)).into()) + .unwrap(), + false + ); // when block is not connected to the end block let storage = DummyStorage::new() - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)) .with_header(fork_header(0, 3, 4)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .matches(&storage, (&fork_id(0, 3, 4)).into()).unwrap(), false); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .matches(&storage, (&fork_id(0, 3, 4)).into()) + .unwrap(), + false + ); // when block is connected to the begin block AND end is open let storage = DummyStorage::new() .with_entry(correct_id(5), StorageEntry { prev_valid_from: None, value: 100 }) .with_header(test_header(5)) .with_header(test_header(6)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .matches(&storage, (&correct_id(6)).into()).unwrap(), true); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .matches(&storage, (&correct_id(6)).into()) + .unwrap(), + true + ); // when block is connected to the begin block AND to the end block let storage = DummyStorage::new() - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .matches(&storage, (&correct_id(4)).into()).unwrap(), true); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .matches(&storage, (&correct_id(4)).into()) + .unwrap(), + true + ); } #[test] fn fork_try_append_works() { // when best block is unknown - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .try_append(&test_id(100)), false); + assert_eq!( + Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } + .try_append(&test_id(100)), + false + ); // when best block is known but different - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .try_append(&test_id(101)), false); + assert_eq!( + Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } + .try_append(&test_id(101)), + false + ); // when best block is known and the same - assert_eq!(Fork::<_, u64> { best_block: Some(test_id(100)), head: Entry { valid_from: test_id(100), value: 0 } } - .try_append(&test_id(100)), true); + assert_eq!( + Fork::<_, u64> { + best_block: Some(test_id(100)), + head: Entry { valid_from: test_id(100), value: 0 } + } + .try_append(&test_id(100)), + true + ); } #[test] fn fork_try_append_or_fork_works() { // when there's no entry before parent let storage = DummyStorage::new() - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }, + ) .with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .try_append_or_fork(&storage, &test_id(30), None).unwrap(), None); + assert_eq!( + Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } + .try_append_or_fork(&storage, &test_id(30), None) + .unwrap(), + None + ); // when parent does not belong to the fork let storage = DummyStorage::new() - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)) .with_header(fork_header(0, 2, 4)) .with_header(fork_header(0, 2, 3)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .try_append_or_fork(&storage, &fork_id(0, 2, 4), None).unwrap(), None); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .try_append_or_fork(&storage, &fork_id(0, 2, 4), None) + .unwrap(), + None + ); // when the entry before parent is the head entry let storage = DummyStorage::new() .with_entry( @@ -1464,30 +1878,57 @@ mod tests { ) .with_header(test_header(6)) .with_header(test_header(5)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(5), value: 100 } } - .try_append_or_fork(&storage, &correct_id(6), None).unwrap(), Some(ForkAppendResult::Append)); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(5), value: 100 } + } + .try_append_or_fork(&storage, &correct_id(6), None) + .unwrap(), + Some(ForkAppendResult::Append) + ); // when the parent located after last finalized entry let storage = DummyStorage::new() - .with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(6), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(6)) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)) .with_header(fork_header(0, 4, 5)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: 100 } } - .try_append_or_fork(&storage, &fork_id(0, 4, 5), None).unwrap(), Some(ForkAppendResult::Fork(ComplexBlockId::new(test_header(3).hash(), 3)))); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(6), value: 100 } + } + .try_append_or_fork(&storage, &fork_id(0, 4, 5), None) + .unwrap(), + Some(ForkAppendResult::Fork(ComplexBlockId::new(test_header(3).hash(), 3))) + ); // when the parent located before last finalized entry let storage = DummyStorage::new() - .with_entry(correct_id(6), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }) + .with_entry( + correct_id(6), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 100 }, + ) .with_entry(correct_id(3), StorageEntry { prev_valid_from: None, value: 200 }) .with_header(test_header(6)) .with_header(test_header(5)) .with_header(test_header(4)) .with_header(test_header(3)) .with_header(fork_header(0, 4, 5)); - assert_eq!(Fork::<_, u64> { best_block: None, head: Entry { valid_from: correct_id(6), value: 100 } } - .try_append_or_fork(&storage, &fork_id(0, 4, 5), Some(3)).unwrap(), None); + assert_eq!( + Fork::<_, u64> { + best_block: None, + head: Entry { valid_from: correct_id(6), value: 100 } + } + .try_append_or_fork(&storage, &fork_id(0, 4, 5), Some(3)) + .unwrap(), + None + ); } #[test] @@ -1496,12 +1937,16 @@ mod tests { let storage = DummyStorage::new().with_id(100, H256::from_low_u64_be(100)); let mut tx = DummyTransaction::new(); Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .destroy(&storage, &mut tx, Some(200)).unwrap(); + .destroy(&storage, &mut tx, Some(200)) + .unwrap(); assert!(tx.removed_entries().is_empty()); // when we reach finalized entry with iterations let storage = DummyStorage::new() .with_id(10, H256::from_low_u64_be(10)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }, + ) .with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(20)), value: 50 }) .with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: 20 }) .with_entry(test_id(10), StorageEntry { prev_valid_from: Some(test_id(5)), value: 10 }) @@ -1509,120 +1954,192 @@ mod tests { .with_entry(test_id(3), StorageEntry { prev_valid_from: None, value: 0 }); let mut tx = DummyTransaction::new(); Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .destroy(&storage, &mut tx, Some(200)).unwrap(); - assert_eq!(*tx.removed_entries(), - vec![test_id(100).hash, test_id(50).hash, test_id(20).hash].into_iter().collect()); + .destroy(&storage, &mut tx, Some(200)) + .unwrap(); + assert_eq!( + *tx.removed_entries(), + vec![test_id(100).hash, test_id(50).hash, test_id(20).hash] + .into_iter() + .collect() + ); // when we reach beginning of fork before finalized block let storage = DummyStorage::new() .with_id(10, H256::from_low_u64_be(10)) - .with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }) + .with_entry( + test_id(100), + StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }, + ) .with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }); let mut tx = DummyTransaction::new(); Fork::<_, u64> { best_block: None, head: Entry { valid_from: test_id(100), value: 0 } } - .destroy(&storage, &mut tx, Some(200)).unwrap(); - assert_eq!(*tx.removed_entries(), - vec![test_id(100).hash, test_id(50).hash].into_iter().collect()); + .destroy(&storage, &mut tx, Some(200)) + .unwrap(); + assert_eq!( + *tx.removed_entries(), + vec![test_id(100).hash, test_id(50).hash].into_iter().collect() + ); } #[test] fn is_connected_to_block_fails() { // when storage returns error - assert!( - chain::is_connected_to_block::<_, u64, _>( - &FaultyStorage, - (&test_id(1)).into(), - &test_id(100), - ).is_err(), - ); + assert!(chain::is_connected_to_block::<_, u64, _>( + &FaultyStorage, + (&test_id(1)).into(), + &test_id(100), + ) + .is_err(),); // when there's no header in the storage - assert!( - chain::is_connected_to_block::<_, u64, _>( - &DummyStorage::new(), - (&test_id(1)).into(), - &test_id(100), - ).is_err(), - ); + assert!(chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new(), + (&test_id(1)).into(), + &test_id(100), + ) + .is_err(),); } #[test] fn is_connected_to_block_works() { // when without iterations we end up with different block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(1)), - (&test_id(1)).into(), &correct_id(1)).unwrap(), false); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new().with_header(test_header(1)), + (&test_id(1)).into(), + &correct_id(1) + ) + .unwrap(), + false + ); // when with ASC iterations we end up with different block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(0)) - .with_header(test_header(1)) - .with_header(test_header(2)), - (&test_id(0)).into(), &correct_id(2)).unwrap(), false); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new() + .with_header(test_header(0)) + .with_header(test_header(1)) + .with_header(test_header(2)), + (&test_id(0)).into(), + &correct_id(2) + ) + .unwrap(), + false + ); // when with DESC iterations we end up with different block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(0)) - .with_header(test_header(1)) - .with_header(test_header(2)), - (&correct_id(2)).into(), &test_id(0)).unwrap(), false); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new() + .with_header(test_header(0)) + .with_header(test_header(1)) + .with_header(test_header(2)), + (&correct_id(2)).into(), + &test_id(0) + ) + .unwrap(), + false + ); // when without iterations we end up with the same block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(1)), - (&correct_id(1)).into(), &correct_id(1)).unwrap(), true); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new().with_header(test_header(1)), + (&correct_id(1)).into(), + &correct_id(1) + ) + .unwrap(), + true + ); // when with ASC iterations we end up with the same block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(0)) - .with_header(test_header(1)) - .with_header(test_header(2)), - (&correct_id(0)).into(), &correct_id(2)).unwrap(), true); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new() + .with_header(test_header(0)) + .with_header(test_header(1)) + .with_header(test_header(2)), + (&correct_id(0)).into(), + &correct_id(2) + ) + .unwrap(), + true + ); // when with DESC iterations we end up with the same block - assert_eq!(chain::is_connected_to_block::<_, u64, _>(&DummyStorage::new() - .with_header(test_header(0)) - .with_header(test_header(1)) - .with_header(test_header(2)), - (&correct_id(2)).into(), &correct_id(0)).unwrap(), true); + assert_eq!( + chain::is_connected_to_block::<_, u64, _>( + &DummyStorage::new() + .with_header(test_header(0)) + .with_header(test_header(1)) + .with_header(test_header(2)), + (&correct_id(2)).into(), + &correct_id(0) + ) + .unwrap(), + true + ); } #[test] fn is_finalized_block_fails() { // when storage returns error assert!(chain::is_finalized_block::<_, u64, _>(&FaultyStorage, &test_id(1), 100).is_err()); - } #[test] fn is_finalized_block_works() { // when number of block is larger than last finalized block - assert_eq!(chain::is_finalized_block::<_, u64, _>(&DummyStorage::new(), &test_id(100), 1).unwrap(), false); + assert_eq!( + chain::is_finalized_block::<_, u64, _>(&DummyStorage::new(), &test_id(100), 1).unwrap(), + false + ); // when there's no hash for this block number in the database - assert_eq!(chain::is_finalized_block::<_, u64, _>(&DummyStorage::new(), &test_id(1), 100).unwrap(), false); + assert_eq!( + chain::is_finalized_block::<_, u64, _>(&DummyStorage::new(), &test_id(1), 100).unwrap(), + false + ); // when there's different hash for this block number in the database - assert_eq!(chain::is_finalized_block::<_, u64, _>(&DummyStorage::new() - .with_id(1, H256::from_low_u64_be(2)), &test_id(1), 100).unwrap(), false); + assert_eq!( + chain::is_finalized_block::<_, u64, _>( + &DummyStorage::new().with_id(1, H256::from_low_u64_be(2)), + &test_id(1), + 100 + ) + .unwrap(), + false + ); // when there's the same hash for this block number in the database - assert_eq!(chain::is_finalized_block::<_, u64, _>(&DummyStorage::new() - .with_id(1, H256::from_low_u64_be(1)), &test_id(1), 100).unwrap(), true); + assert_eq!( + chain::is_finalized_block::<_, u64, _>( + &DummyStorage::new().with_id(1, H256::from_low_u64_be(1)), + &test_id(1), + 100 + ) + .unwrap(), + true + ); } #[test] fn read_forks_fails() { // when storage returns error during finalized entry read - assert!(read_forks::(&FaultyStorage, Metadata { - finalized: Some(test_id(1)), - unfinalized: vec![], - }).is_err()); + assert!(read_forks::( + &FaultyStorage, + Metadata { finalized: Some(test_id(1)), unfinalized: vec![] } + ) + .is_err()); // when storage returns error during unfinalized entry read - assert!(read_forks::(&FaultyStorage, Metadata { - finalized: None, - unfinalized: vec![test_id(1)], - }).is_err()); + assert!(read_forks::( + &FaultyStorage, + Metadata { finalized: None, unfinalized: vec![test_id(1)] } + ) + .is_err()); // when finalized entry is not found - assert!(read_forks::(&DummyStorage::new(), Metadata { - finalized: Some(test_id(1)), - unfinalized: vec![], - }).is_err()); + assert!(read_forks::( + &DummyStorage::new(), + Metadata { finalized: Some(test_id(1)), unfinalized: vec![] } + ) + .is_err()); // when unfinalized entry is not found - assert!(read_forks::(&DummyStorage::new(), Metadata { - finalized: None, - unfinalized: vec![test_id(1)], - }).is_err()); + assert!(read_forks::( + &DummyStorage::new(), + Metadata { finalized: None, unfinalized: vec![test_id(1)] } + ) + .is_err()); } #[test] @@ -1639,23 +2156,40 @@ mod tests { ], ); - assert_eq!(expected, read_forks(&storage, Metadata { - finalized: Some(test_id(10)), - unfinalized: vec![test_id(20), test_id(30)], - }).unwrap()); + assert_eq!( + expected, + read_forks( + &storage, + Metadata { + finalized: Some(test_id(10)), + unfinalized: vec![test_id(20), test_id(30)], + } + ) + .unwrap() + ); } #[test] fn ancient_entries_are_pruned_when_pruning_enabled() { fn do_test(strategy: PruningStrategy) { - let cache = ListCache::new(DummyStorage::new() - .with_id(10, H256::from_low_u64_be(10)) - .with_id(20, H256::from_low_u64_be(20)) - .with_id(30, H256::from_low_u64_be(30)) - .with_entry(test_id(10), StorageEntry { prev_valid_from: None, value: 10 }) - .with_entry(test_id(20), StorageEntry { prev_valid_from: Some(test_id(10)), value: 20 }) - .with_entry(test_id(30), StorageEntry { prev_valid_from: Some(test_id(20)), value: 30 }), - strategy, test_id(9)).unwrap(); + let cache = ListCache::new( + DummyStorage::new() + .with_id(10, H256::from_low_u64_be(10)) + .with_id(20, H256::from_low_u64_be(20)) + .with_id(30, H256::from_low_u64_be(30)) + .with_entry(test_id(10), StorageEntry { prev_valid_from: None, value: 10 }) + .with_entry( + test_id(20), + StorageEntry { prev_valid_from: Some(test_id(10)), value: 20 }, + ) + .with_entry( + test_id(30), + StorageEntry { prev_valid_from: Some(test_id(20)), value: 30 }, + ), + strategy, + test_id(9), + ) + .unwrap(); let mut tx = DummyTransaction::new(); // when finalizing entry #10: no entries pruned @@ -1679,7 +2213,10 @@ mod tests { }, PruningStrategy::ByDepth(_) => { assert_eq!(*tx.removed_entries(), vec![test_id(10).hash].into_iter().collect()); - assert_eq!(*tx.inserted_entries(), vec![test_id(20).hash].into_iter().collect()); + assert_eq!( + *tx.inserted_entries(), + vec![test_id(20).hash].into_iter().collect() + ); }, } } @@ -1697,15 +2234,36 @@ mod tests { // -> (3') -> 4' -> 5' let mut cache = ListCache::new( DummyStorage::new() - .with_meta(Some(correct_id(1)), vec![correct_id(5), fork_id(1, 2, 5), fork_id(2, 4, 5)]) + .with_meta( + Some(correct_id(1)), + vec![correct_id(5), fork_id(1, 2, 5), fork_id(2, 4, 5)], + ) .with_id(1, correct_id(1).hash) .with_entry(correct_id(1), StorageEntry { prev_valid_from: None, value: 1 }) - .with_entry(correct_id(3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 3 }) - .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 4 }) - .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(4)), value: 5 }) - .with_entry(fork_id(1, 2, 4), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 14 }) - .with_entry(fork_id(1, 2, 5), StorageEntry { prev_valid_from: Some(fork_id(1, 2, 4)), value: 15 }) - .with_entry(fork_id(2, 4, 5), StorageEntry { prev_valid_from: Some(correct_id(4)), value: 25 }) + .with_entry( + correct_id(3), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 3 }, + ) + .with_entry( + correct_id(4), + StorageEntry { prev_valid_from: Some(correct_id(3)), value: 4 }, + ) + .with_entry( + correct_id(5), + StorageEntry { prev_valid_from: Some(correct_id(4)), value: 5 }, + ) + .with_entry( + fork_id(1, 2, 4), + StorageEntry { prev_valid_from: Some(correct_id(1)), value: 14 }, + ) + .with_entry( + fork_id(1, 2, 5), + StorageEntry { prev_valid_from: Some(fork_id(1, 2, 4)), value: 15 }, + ) + .with_entry( + fork_id(2, 4, 5), + StorageEntry { prev_valid_from: Some(correct_id(4)), value: 25 }, + ) .with_header(test_header(1)) .with_header(test_header(2)) .with_header(test_header(3)) @@ -1715,29 +2273,40 @@ mod tests { .with_header(fork_header(1, 2, 4)) .with_header(fork_header(1, 2, 5)) .with_header(fork_header(2, 4, 5)), - PruningStrategy::ByDepth(1024), correct_id(1) - ).unwrap(); + PruningStrategy::ByDepth(1024), + correct_id(1), + ) + .unwrap(); // when 5 is reverted: entry 5 is truncated let op = cache.do_on_block_revert(&mut DummyTransaction::new(), &correct_id(5)).unwrap(); - assert_eq!(op, CommitOperation::BlockReverted(vec![ - (0, Some(Fork { best_block: None, head: Entry { valid_from: correct_id(4), value: 4 } })), - ].into_iter().collect())); + assert_eq!( + op, + CommitOperation::BlockReverted( + vec![( + 0, + Some(Fork { + best_block: None, + head: Entry { valid_from: correct_id(4), value: 4 } + }) + ),] + .into_iter() + .collect() + ) + ); cache.on_transaction_commit(vec![op].into()); // when 3 is reverted: entries 4+5' are truncated let op = cache.do_on_block_revert(&mut DummyTransaction::new(), &correct_id(3)).unwrap(); - assert_eq!(op, CommitOperation::BlockReverted(vec![ - (0, None), - (2, None), - ].into_iter().collect())); + assert_eq!( + op, + CommitOperation::BlockReverted(vec![(0, None), (2, None),].into_iter().collect()) + ); cache.on_transaction_commit(vec![op].into()); // when 2 is reverted: entries 4'+5' are truncated let op = cache.do_on_block_revert(&mut DummyTransaction::new(), &correct_id(2)).unwrap(); - assert_eq!(op, CommitOperation::BlockReverted(vec![ - (0, None), - ].into_iter().collect())); + assert_eq!(op, CommitOperation::BlockReverted(vec![(0, None),].into_iter().collect())); cache.on_transaction_commit(vec![op].into()); } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f1458472e024a..2587a64df6046 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -34,63 +34,72 @@ pub mod offchain; #[cfg(any(feature = "with-kvdb-rocksdb", test))] pub mod bench; -mod children; mod cache; mod changes_tries_storage; +mod children; +#[cfg(feature = "with-parity-db")] +mod parity_db; +mod stats; mod storage_cache; #[cfg(any(feature = "with-kvdb-rocksdb", test))] mod upgrade; mod utils; -mod stats; -#[cfg(feature = "with-parity-db")] -mod parity_db; -use std::sync::Arc; -use std::path::{Path, PathBuf}; -use std::io; -use std::collections::{HashMap, HashSet}; -use parking_lot::{Mutex, RwLock}; use linked_hash_map::LinkedHashMap; -use log::{trace, debug, warn}; +use log::{debug, trace, warn}; +use parking_lot::{Mutex, RwLock}; +use std::{ + collections::{HashMap, HashSet}, + io, + path::{Path, PathBuf}, + sync::Arc, +}; +use crate::{ + changes_tries_storage::{DbChangesTrieStorage, DbChangesTrieStorageTransaction}, + stats::StateUsageStats, + storage_cache::{new_shared_cache, CachingState, SharedCache, SyncingCachingState}, + utils::{meta_keys, read_db, read_meta, DatabaseType, Meta}, +}; +use codec::{Decode, Encode}; +use hash_db::Prefix; use sc_client_api::{ - UsageInfo, MemoryInfo, IoInfo, MemorySize, - backend::{NewBlockState, PrunableStateChangesTrieStorage, ProvideChtRoots}, - leaves::{LeafSet, FinalizationDisplaced}, cht, + backend::{NewBlockState, ProvideChtRoots, PrunableStateChangesTrieStorage}, + cht, + leaves::{FinalizationDisplaced, LeafSet}, utils::is_descendent_of, + IoInfo, MemoryInfo, MemorySize, UsageInfo, }; +use sc_state_db::StateDb; +use sp_arithmetic::traits::Saturating; use sp_blockchain::{ - Result as ClientResult, Error as ClientError, - well_known_cache_keys, Backend as _, HeaderBackend, + well_known_cache_keys, Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend, + HeaderMetadata, HeaderMetadataCache, Result as ClientResult, +}; +use sp_core::{ + offchain::OffchainOverlayedChange, + storage::{well_known_keys, ChildInfo}, + ChangesTrieConfiguration, }; -use codec::{Decode, Encode}; -use hash_db::Prefix; -use sp_trie::{MemoryDB, PrefixedMemoryDB, prefixed_key}; use sp_database::Transaction; -use sp_core::ChangesTrieConfiguration; -use sp_core::offchain::OffchainOverlayedChange; -use sp_core::storage::{well_known_keys, ChildInfo}; -use sp_arithmetic::traits::Saturating; -use sp_runtime::{generic::{DigestItem, BlockId}, Justification, Justifications, Storage}; -use sp_runtime::traits::{ - Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor, - Hash, +use sp_runtime::{ + generic::{BlockId, DigestItem}, + traits::{ + Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion, + Zero, + }, + Justification, Justifications, Storage, }; use sp_state_machine::{ - DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, UsageInfo as StateUsageInfo, - StorageCollection, ChildStorageCollection, OffchainChangesCollection, - backend::Backend as StateBackend, StateMachineStats, IndexOperation, + backend::Backend as StateBackend, ChangesTrieCacheAction, ChangesTrieTransaction, + ChildStorageCollection, DBValue, IndexOperation, OffchainChangesCollection, StateMachineStats, + StorageCollection, UsageInfo as StateUsageInfo, }; -use crate::utils::{DatabaseType, Meta, meta_keys, read_db, read_meta}; -use crate::changes_tries_storage::{DbChangesTrieStorage, DbChangesTrieStorageTransaction}; -use sc_state_db::StateDb; -use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache}; -use crate::storage_cache::{CachingState, SyncingCachingState, SharedCache, new_shared_cache}; -use crate::stats::StateUsageStats; +use sp_trie::{prefixed_key, MemoryDB, PrefixedMemoryDB}; // Re-export the Database trait so that one can pass an implementation of it. -pub use sp_database::Database; pub use sc_state_db::PruningMode; +pub use sp_database::Database; #[cfg(any(feature = "with-kvdb-rocksdb", test))] pub use bench::BenchmarkingState; @@ -102,9 +111,8 @@ const CACHE_HEADERS: usize = 8; const DEFAULT_CHILD_RATIO: (usize, usize) = (1, 10); /// DB-backed patricia trie state, transaction type is an overlay of changes to commit. -pub type DbState = sp_state_machine::TrieBackend< - Arc>>, HashFor ->; +pub type DbState = + sp_state_machine::TrieBackend>>, HashFor>; const DB_HASH_LEN: usize = 32; /// Hash type that this backend uses for the database. @@ -131,11 +139,7 @@ pub struct RefTrackingState { impl RefTrackingState { fn new(state: DbState, storage: Arc>, parent_hash: Option) -> Self { - RefTrackingState { - state, - parent_hash, - storage, - } + RefTrackingState { state, parent_hash, storage } } } @@ -154,7 +158,7 @@ impl std::fmt::Debug for RefTrackingState { } impl StateBackend> for RefTrackingState { - type Error = as StateBackend>>::Error; + type Error = as StateBackend>>::Error; type Transaction = as StateBackend>>::Transaction; type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; @@ -214,7 +218,8 @@ impl StateBackend> for RefTrackingState { f: F, allow_missing: bool, ) -> Result { - self.state.apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) + self.state + .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) } fn apply_to_keys_while bool>( @@ -237,16 +242,22 @@ impl StateBackend> for RefTrackingState { fn storage_root<'a>( &self, - delta: impl Iterator)>, - ) -> (B::Hash, Self::Transaction) where B::Hash: Ord { + delta: impl Iterator)>, + ) -> (B::Hash, Self::Transaction) + where + B::Hash: Ord, + { self.state.storage_root(delta) } fn child_storage_root<'a>( &self, child_info: &ChildInfo, - delta: impl Iterator)>, - ) -> (B::Hash, bool, Self::Transaction) where B::Hash: Ord { + delta: impl Iterator)>, + ) -> (B::Hash, bool, Self::Transaction) + where + B::Hash: Ord, + { self.state.child_storage_root(child_info, delta) } @@ -258,17 +269,13 @@ impl StateBackend> for RefTrackingState { self.state.keys(prefix) } - fn child_keys( - &self, - child_info: &ChildInfo, - prefix: &[u8], - ) -> Vec> { + fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { self.state.child_keys(child_info, prefix) } - fn as_trie_backend(&mut self) - -> Option<&sp_state_machine::TrieBackend>> - { + fn as_trie_backend( + &mut self, + ) -> Option<&sp_state_machine::TrieBackend>> { self.state.as_trie_backend() } @@ -432,7 +439,7 @@ pub struct BlockchainDb { impl BlockchainDb { fn new( db: Arc>, - transaction_storage: TransactionStorageMode + transaction_storage: TransactionStorageMode, ) -> ClientResult { let meta = read_meta::(&*db, columns::HEADER)?; let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?; @@ -446,10 +453,7 @@ impl BlockchainDb { }) } - fn update_meta( - &self, - update: MetaUpdate, - ) { + fn update_meta(&self, update: MetaUpdate) { let MetaUpdate { hash, number, is_best, is_finalized, with_state } = update; let mut meta = self.meta.write(); if number.is_zero() { @@ -473,10 +477,9 @@ impl BlockchainDb { // Get block changes trie root, if available. fn changes_trie_root(&self, block: BlockId) -> ClientResult> { - self.header(block) - .map(|header| header.and_then(|header| - header.digest().log(DigestItem::as_changes_trie_root) - .cloned())) + self.header(block).map(|header| { + header.and_then(|header| header.digest().log(DigestItem::as_changes_trie_root).cloned()) + }) } } @@ -486,15 +489,15 @@ impl sc_client_api::blockchain::HeaderBackend for Blockcha BlockId::Hash(h) => { let mut cache = self.header_cache.lock(); if let Some(result) = cache.get_refresh(h) { - return Ok(result.clone()); + return Ok(result.clone()) } - let header = utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)?; + let header = + utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)?; cache_header(&mut cache, h.clone(), header.clone()); Ok(header) - } - BlockId::Number(_) => { - utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id) - } + }, + BlockId::Number(_) => + utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id), } } @@ -527,10 +530,11 @@ impl sc_client_api::blockchain::HeaderBackend for Blockcha } fn hash(&self, number: NumberFor) -> ClientResult> { - self.header(BlockId::Number(number)).and_then(|maybe_header| match maybe_header { - Some(header) => Ok(Some(header.hash().clone())), - None => Ok(None), - }) + self.header(BlockId::Number(number)) + .and_then(|maybe_header| match maybe_header { + Some(header) => Ok(Some(header.hash().clone())), + None => Ok(None), + }) } } @@ -543,40 +547,51 @@ impl sc_client_api::blockchain::Backend for BlockchainDb match Decode::decode(&mut &body[..]) { Ok(body) => Ok(Some(body)), - Err(err) => return Err(sp_blockchain::Error::Backend( - format!("Error decoding body: {}", err) - )), + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding body: {}", + err + ))), }, TransactionStorageMode::StorageChain => { match Vec::::decode(&mut &body[..]) { Ok(index) => { - let extrinsics: ClientResult> = index.into_iter().map( - | ExtrinsicHeader { indexed_hash, data } | { + let extrinsics: ClientResult> = index + .into_iter() + .map(|ExtrinsicHeader { indexed_hash, data }| { let decode_result = if indexed_hash != Default::default() { match self.db.get(columns::TRANSACTION, indexed_hash.as_ref()) { Some(t) => { - let mut input = utils::join_input(data.as_ref(), t.as_ref()); + let mut input = + utils::join_input(data.as_ref(), t.as_ref()); Block::Extrinsic::decode(&mut input) }, - None => return Err(sp_blockchain::Error::Backend( - format!("Missing indexed transaction {:?}", indexed_hash)) - ) + None => + return Err(sp_blockchain::Error::Backend(format!( + "Missing indexed transaction {:?}", + indexed_hash + ))), } } else { Block::Extrinsic::decode(&mut data.as_ref()) }; - decode_result.map_err(|err| sp_blockchain::Error::Backend( - format!("Error decoding extrinsic: {}", err)) - ) - } - ).collect(); + decode_result.map_err(|err| { + sp_blockchain::Error::Backend(format!( + "Error decoding extrinsic: {}", + err + )) + }) + }) + .collect(); Ok(Some(extrinsics?)) - } - Err(err) => return Err(sp_blockchain::Error::Backend( - format!("Error decoding body list: {}", err) - )), + }, + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding body list: {}", + err + ))), } - } + }, } } @@ -584,10 +599,12 @@ impl sc_client_api::blockchain::Backend for BlockchainDb match Decode::decode(&mut &justifications[..]) { Ok(justifications) => Ok(Some(justifications)), - Err(err) => return Err(sp_blockchain::Error::Backend( - format!("Error decoding justifications: {}", err) - )), - } + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding justifications: {}", + err + ))), + }, None => Ok(None), } } @@ -631,19 +648,23 @@ impl sc_client_api::blockchain::Backend for BlockchainDb transactions.push(t), - None => return Err(sp_blockchain::Error::Backend( - format!("Missing indexed transaction {:?}", indexed_hash)) - ) + None => + return Err(sp_blockchain::Error::Backend(format!( + "Missing indexed transaction {:?}", + indexed_hash + ))), } } } Ok(Some(transactions)) - } - Err(err) => return Err(sp_blockchain::Error::Backend( - format!("Error decoding body list: {}", err) - )), + }, + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding body list: {}", + err + ))), } - } + }, } } } @@ -657,17 +678,28 @@ impl sc_client_api::blockchain::ProvideCache for Blockchai impl HeaderMetadata for BlockchainDb { type Error = sp_blockchain::Error; - fn header_metadata(&self, hash: Block::Hash) -> Result, Self::Error> { - self.header_metadata_cache.header_metadata(hash).map_or_else(|| { - self.header(BlockId::hash(hash))?.map(|header| { - let header_metadata = CachedHeaderMetadata::from(&header); - self.header_metadata_cache.insert_header_metadata( - header_metadata.hash, - header_metadata.clone(), - ); - header_metadata - }).ok_or_else(|| ClientError::UnknownBlock(format!("Header was not found in the database: {:?}", hash))) - }, Ok) + fn header_metadata( + &self, + hash: Block::Hash, + ) -> Result, Self::Error> { + self.header_metadata_cache.header_metadata(hash).map_or_else( + || { + self.header(BlockId::hash(hash))? + .map(|header| { + let header_metadata = CachedHeaderMetadata::from(&header); + self.header_metadata_cache + .insert_header_metadata(header_metadata.hash, header_metadata.clone()); + header_metadata + }) + .ok_or_else(|| { + ClientError::UnknownBlock(format!( + "Header was not found in the database: {:?}", + hash + )) + }) + }, + Ok, + ) } fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata) { @@ -701,8 +733,11 @@ impl ProvideChtRoots for BlockchainDb { }); cht::compute_root::, _>( - cht::size(), cht_number, cht_range.map(|num| self.hash(num)) - ).map(Some) + cht::size(), + cht_number, + cht_range.map(|num| self.hash(num)), + ) + .map(Some) } fn changes_trie_cht_root( @@ -728,7 +763,8 @@ impl ProvideChtRoots for BlockchainDb { cht::size(), cht_number, cht_range.map(|num| self.changes_trie_root(BlockId::Number(num))), - ).map(Some) + ) + .map(Some) } } @@ -759,8 +795,7 @@ impl BlockImportOperation { match value_operation { OffchainOverlayedChange::SetValue(val) => transaction.set_from_vec(columns::OFFCHAIN, &key, val), - OffchainOverlayedChange::Remove => - transaction.remove(columns::OFFCHAIN, &key), + OffchainOverlayedChange::Remove => transaction.remove(columns::OFFCHAIN, &key), } } @@ -778,18 +813,17 @@ impl BlockImportOperation { } } - fn apply_new_state( - &mut self, - storage: Storage, - ) -> ClientResult { + fn apply_new_state(&mut self, storage: Storage) -> ClientResult { if storage.top.keys().any(|k| well_known_keys::is_child_storage_key(&k)) { - return Err(sp_blockchain::Error::InvalidState.into()); + return Err(sp_blockchain::Error::InvalidState.into()) } - let child_delta = storage.children_default.iter().map(|(_storage_key, child_content)|( + let child_delta = storage.children_default.iter().map(|(_storage_key, child_content)| { + ( &child_content.child_info, child_content.data.iter().map(|(k, v)| (&k[..], Some(&v[..]))), - )); + ) + }); let mut changes_trie_config = None; let (root, transaction) = self.old_state.full_storage_root( @@ -799,7 +833,7 @@ impl BlockImportOperation { } (&k[..], Some(&v[..])) }), - child_delta + child_delta, ); let changes_trie_config = match changes_trie_config { @@ -812,10 +846,11 @@ impl BlockImportOperation { self.changes_trie_config_update = Some(changes_trie_config); Ok(root) } - } -impl sc_client_api::backend::BlockImportOperation for BlockImportOperation { +impl sc_client_api::backend::BlockImportOperation + for BlockImportOperation +{ type State = SyncingCachingState, Block>; fn state(&self) -> ClientResult> { @@ -831,16 +866,13 @@ impl sc_client_api::backend::BlockImportOperation for Bloc leaf_state: NewBlockState, ) -> ClientResult<()> { assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); - if let Some(changes_trie_config_update) = changes_tries_storage::extract_new_configuration(&header) { + if let Some(changes_trie_config_update) = + changes_tries_storage::extract_new_configuration(&header) + { self.changes_trie_config_update = Some(changes_trie_config_update.clone()); } - self.pending_block = Some(PendingBlock { - header, - body, - indexed_body, - justifications, - leaf_state, - }); + self.pending_block = + Some(PendingBlock { header, body, indexed_body, justifications, leaf_state }); Ok(()) } @@ -853,20 +885,13 @@ impl sc_client_api::backend::BlockImportOperation for Bloc Ok(()) } - fn reset_storage( - &mut self, - storage: Storage, - ) -> ClientResult { + fn reset_storage(&mut self, storage: Storage) -> ClientResult { let root = self.apply_new_state(storage)?; self.commit_state = true; Ok(root) } - fn set_genesis_state( - &mut self, - storage: Storage, - commit: bool, - ) -> ClientResult { + fn set_genesis_state(&mut self, storage: Storage, commit: bool) -> ClientResult { let root = self.apply_new_state(storage)?; self.commit_state = commit; Ok(root) @@ -882,7 +907,8 @@ impl sc_client_api::backend::BlockImportOperation for Bloc } fn insert_aux(&mut self, ops: I) -> ClientResult<()> - where I: IntoIterator, Option>)> + where + I: IntoIterator, Option>)>, { self.aux_ops.append(&mut ops.into_iter().collect()); Ok(()) @@ -961,10 +987,7 @@ struct DbGenesisStorage { impl DbGenesisStorage { pub fn new(root: Block::Hash, storage: PrefixedMemoryDB>) -> Self { - DbGenesisStorage { - root, - storage, - } + DbGenesisStorage { root, storage } } } @@ -1012,13 +1035,13 @@ pub(crate) struct FrozenForDuration { impl FrozenForDuration { fn new(duration: std::time::Duration) -> Self { - Self { - duration, - value: Frozen { at: std::time::Instant::now(), value: None }.into(), - } + Self { duration, value: Frozen { at: std::time::Instant::now(), value: None }.into() } } - fn take_or_else(&self, f: F) -> T where F: FnOnce() -> T { + fn take_or_else(&self, f: F) -> T + where + F: FnOnce() -> T, + { let mut lock = self.value.lock(); if lock.at.elapsed() > self.duration || lock.value.is_none() { let new_value = f(); @@ -1104,7 +1127,8 @@ impl Backend { config.state_pruning.clone(), !config.source.supports_ref_counting(), &StateMetaDb(&*db), - ).map_err(map_e)?; + ) + .map_err(map_e)?; let storage_db = StorageDb { db: db.clone(), state_db, @@ -1120,11 +1144,7 @@ impl Backend { columns::HEADER, columns::CACHE, meta, - if is_archive_pruning { - None - } else { - Some(MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR) - }, + if is_archive_pruning { None } else { Some(MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR) }, )?; let backend = Backend { @@ -1148,10 +1168,13 @@ impl Backend { // Older DB versions have no last state key. Check if the state is available and set it. let info = backend.blockchain.info(); - if info.finalized_state.is_none() - && info.finalized_hash != Default::default() - && sc_client_api::Backend::have_state_at(&backend, &info.finalized_hash, info.finalized_number) - { + if info.finalized_state.is_none() && + info.finalized_hash != Default::default() && + sc_client_api::Backend::have_state_at( + &backend, + &info.finalized_hash, + info.finalized_number, + ) { backend.blockchain.update_meta(MetaUpdate { hash: info.finalized_hash, number: info.finalized_number, @@ -1190,8 +1213,8 @@ impl Backend { return Err(sp_blockchain::Error::SetHeadTooOld.into()) } - let parent_exists = self.blockchain.status(BlockId::Hash(route_to))? - == sp_blockchain::BlockStatus::InChain; + let parent_exists = + self.blockchain.status(BlockId::Hash(route_to))? == sp_blockchain::BlockStatus::InChain; // Cannot find tree route with empty DB or when imported a detached block. if meta.best_hash != Default::default() && parent_exists { @@ -1206,15 +1229,11 @@ impl Backend { (&r.number, &r.hash) ); - return Err(::sp_blockchain::Error::NotInFinalizedChain.into()); + return Err(::sp_blockchain::Error::NotInFinalizedChain.into()) } retracted.push(r.hash.clone()); - utils::remove_number_to_key_mapping( - transaction, - columns::KEY_LOOKUP, - r.number - )?; + utils::remove_number_to_key_mapping(transaction, columns::KEY_LOOKUP, r.number)?; } // canonicalize: set the number lookup to map to this block's hash. @@ -1224,7 +1243,7 @@ impl Backend { transaction, columns::KEY_LOOKUP, e.number, - e.hash + e.hash, )?; } } @@ -1246,12 +1265,16 @@ impl Backend { header: &Block::Header, last_finalized: Option, ) -> ClientResult<()> { - let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); + let last_finalized = + last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); if last_finalized != self.blockchain.meta.read().genesis_hash { if *header.parent_hash() != last_finalized { - return Err(::sp_blockchain::Error::NonSequentialFinalization( - format!("Last finalized {:?} not parent of {:?}", last_finalized, header.hash()), - ).into()); + return Err(::sp_blockchain::Error::NonSequentialFinalization(format!( + "Last finalized {:?} not parent of {:?}", + last_finalized, + header.hash() + )) + .into()) } } Ok(()) @@ -1289,13 +1312,7 @@ impl Backend { Justifications::from(justification).encode(), ); } - Ok(MetaUpdate { - hash: *hash, - number, - is_best: false, - is_finalized: true, - with_state, - }) + Ok(MetaUpdate { hash: *hash, number, is_best: false, is_finalized: true, with_state }) } // performs forced canonicalization with a delay after importing a non-finalized block. @@ -1304,9 +1321,7 @@ impl Backend { transaction: &mut Transaction, hash: Block::Hash, number: NumberFor, - ) - -> ClientResult<()> - { + ) -> ClientResult<()> { let number_u64 = number.saturated_into::(); if number_u64 > self.canonicalization_delay { let new_canonical = number_u64 - self.canonicalization_delay; @@ -1320,29 +1335,28 @@ impl Backend { sc_client_api::blockchain::HeaderBackend::hash( &self.blockchain, new_canonical.saturated_into(), - )?.ok_or_else(|| sp_blockchain::Error::Backend(format!( - "Can't canonicalize missing block number #{} when importing {:?} (#{})", - new_canonical, - hash, - number, - )))? + )? + .ok_or_else(|| { + sp_blockchain::Error::Backend(format!( + "Can't canonicalize missing block number #{} when importing {:?} (#{})", + new_canonical, hash, number, + )) + })? }; if !sc_client_api::Backend::have_state_at(self, &hash, new_canonical.saturated_into()) { return Ok(()) } trace!(target: "db", "Canonicalize block #{} ({:?})", new_canonical, hash); - let commit = self.storage.state_db.canonicalize_block(&hash) - .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; + let commit = self.storage.state_db.canonicalize_block(&hash).map_err( + |e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e), + )?; apply_state_commit(transaction, commit); } Ok(()) } - fn try_commit_operation( - &self, - mut operation: BlockImportOperation, - ) -> ClientResult<()> { + fn try_commit_operation(&self, mut operation: BlockImportOperation) -> ClientResult<()> { let mut transaction = Transaction::new(); let mut finalization_displaced_leaves = None; @@ -1372,12 +1386,12 @@ impl Backend { } let imported = if let Some(pending_block) = operation.pending_block { - let hash = pending_block.header.hash(); let parent_hash = *pending_block.header.parent_hash(); let number = pending_block.header.number().clone(); - let existing_header = number <= best_num && self.blockchain.header(BlockId::hash(hash))?.is_some(); + let existing_header = + number <= best_num && self.blockchain.header(BlockId::hash(hash))?.is_some(); // blocks are keyed by number + hash. let lookup_key = utils::number_and_hash_to_lookup_key(number, hash)?; @@ -1388,12 +1402,7 @@ impl Backend { (Default::default(), Default::default()) }; - utils::insert_hash_to_key_mapping( - &mut transaction, - columns::KEY_LOOKUP, - number, - hash, - )?; + utils::insert_hash_to_key_mapping(&mut transaction, columns::KEY_LOOKUP, number, hash)?; transaction.set_from_vec(columns::HEADER, &lookup_key, pending_block.header.encode()); if let Some(body) = pending_block.body { @@ -1402,7 +1411,8 @@ impl Backend { transaction.set_from_vec(columns::BODY, &lookup_key, body.encode()); }, TransactionStorageMode::StorageChain => { - let body = apply_index_ops::(&mut transaction, body, operation.index_ops); + let body = + apply_index_ops::(&mut transaction, body, operation.index_ops); transaction.set_from_vec(columns::BODY, &lookup_key, body); }, } @@ -1418,11 +1428,19 @@ impl Backend { } } if let Some(justifications) = pending_block.justifications { - transaction.set_from_vec(columns::JUSTIFICATIONS, &lookup_key, justifications.encode()); + transaction.set_from_vec( + columns::JUSTIFICATIONS, + &lookup_key, + justifications.encode(), + ); } if number.is_zero() { - transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key.clone()); + transaction.set_from_vec( + columns::META, + meta_keys::FINALIZED_BLOCK, + lookup_key.clone(), + ); transaction.set(columns::META, meta_keys::GENESIS_HASH, hash.as_ref()); // for tests, because config is set from within the reset_storage @@ -1437,13 +1455,14 @@ impl Backend { // to bootstrap consensus. It is queried for an initial list of authorities, etc. *self.genesis_state.write() = Some(Arc::new(DbGenesisStorage::new( pending_block.header.state_root().clone(), - operation.db_updates.clone() + operation.db_updates.clone(), ))); } } let finalized = if operation.commit_state { - let mut changeset: sc_state_db::ChangeSet> = sc_state_db::ChangeSet::default(); + let mut changeset: sc_state_db::ChangeSet> = + sc_state_db::ChangeSet::default(); let mut ops: u64 = 0; let mut bytes: u64 = 0; let mut removal: u64 = 0; @@ -1451,7 +1470,7 @@ impl Backend { for (mut key, (val, rc)) in operation.db_updates.drain() { if !self.storage.prefix_keys { // Strip prefix - key.drain(0 .. key.len() - DB_HASH_LEN); + key.drain(0..key.len() - DB_HASH_LEN); }; if rc > 0 { ops += 1; @@ -1460,7 +1479,7 @@ impl Backend { changeset.inserted.push((key, val.to_vec())); } else { changeset.inserted.push((key.clone(), val.to_vec())); - for _ in 0 .. rc - 1 { + for _ in 0..rc - 1 { changeset.inserted.push((key.clone(), Default::default())); } } @@ -1470,7 +1489,7 @@ impl Backend { if rc == -1 { changeset.deleted.push(key); } else { - for _ in 0 .. -rc { + for _ in 0..-rc { changeset.deleted.push(key.clone()); } } @@ -1481,27 +1500,32 @@ impl Backend { let mut ops: u64 = 0; let mut bytes: u64 = 0; - for (key, value) in operation.storage_updates.iter() - .chain(operation.child_storage_updates.iter().flat_map(|(_, s)| s.iter())) { - ops += 1; - bytes += key.len() as u64; - if let Some(v) = value.as_ref() { - bytes += v.len() as u64; - } + for (key, value) in operation + .storage_updates + .iter() + .chain(operation.child_storage_updates.iter().flat_map(|(_, s)| s.iter())) + { + ops += 1; + bytes += key.len() as u64; + if let Some(v) = value.as_ref() { + bytes += v.len() as u64; + } } self.state_usage.tally_writes(ops, bytes); let number_u64 = number.saturated_into::(); - let commit = self.storage.state_db.insert_block( - &hash, - number_u64, - &pending_block.header.parent_hash(), - changeset, - ).map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; + let commit = self + .storage + .state_db + .insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset) + .map_err(|e: sc_state_db::Error| { + sp_blockchain::Error::from_state_db(e) + })?; apply_state_commit(&mut transaction, commit); if number <= last_finalized_num { // Canonicalize in the db when re-importing existing blocks with state. - let commit = self.storage.state_db.canonicalize_block(&hash) - .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; + let commit = self.storage.state_db.canonicalize_block(&hash).map_err( + |e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e), + )?; apply_state_commit(&mut transaction, commit); meta_updates.push(MetaUpdate { hash, @@ -1512,7 +1536,6 @@ impl Backend { }); } - // Check if need to finalize. Genesis is always finalized instantly. let finalized = number_u64 == 0 || pending_block.leaf_state.is_final(); finalized @@ -1568,7 +1591,11 @@ impl Backend { { let mut leaves = self.blockchain.leaves.write(); leaves.import(hash, number, parent_hash); - leaves.prepare_transaction(&mut transaction, columns::META, meta_keys::LEAF_PREFIX); + leaves.prepare_transaction( + &mut transaction, + columns::META, + meta_keys::LEAF_PREFIX, + ); } let mut children = children::read_children( @@ -1602,14 +1629,16 @@ impl Backend { }; let cache_update = if let Some(set_head) = operation.set_head { - if let Some(header) = sc_client_api::blockchain::HeaderBackend::header(&self.blockchain, set_head)? { + if let Some(header) = + sc_client_api::blockchain::HeaderBackend::header(&self.blockchain, set_head)? + { let number = header.number(); let hash = header.hash(); let (enacted, retracted) = self.set_head_with_transaction( &mut transaction, hash.clone(), - (number.clone(), hash.clone()) + (number.clone(), hash.clone()), )?; meta_updates.push(MetaUpdate { hash, @@ -1620,7 +1649,10 @@ impl Backend { }); Some((enacted, retracted)) } else { - return Err(sp_blockchain::Error::UnknownBlock(format!("Cannot set head {:?}", set_head))) + return Err(sp_blockchain::Error::UnknownBlock(format!( + "Cannot set head {:?}", + set_head + ))) } } else { None @@ -1634,10 +1666,7 @@ impl Backend { if let Some((header, number, hash, enacted, retracted, is_best, mut cache)) = imported { trace!(target: "db", "DB Commit done {:?}", hash); let header_metadata = CachedHeaderMetadata::from(&header); - self.blockchain.insert_header_metadata( - header_metadata.hash, - header_metadata, - ); + self.blockchain.insert_header_metadata(header_metadata.hash, header_metadata); cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header)); cache.sync_cache( &enacted, @@ -1688,10 +1717,15 @@ impl Backend { transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key); if sc_client_api::Backend::have_state_at(self, &f_hash, f_num) && - self.storage.state_db.best_canonical().map(|c| f_num.saturated_into::() > c).unwrap_or(true) + self.storage + .state_db + .best_canonical() + .map(|c| f_num.saturated_into::() > c) + .unwrap_or(true) { - let commit = self.storage.state_db.canonicalize_block(&f_hash) - .map_err(|e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e))?; + let commit = self.storage.state_db.canonicalize_block(&f_hash).map_err( + |e: sc_state_db::Error| sp_blockchain::Error::from_state_db(e), + )?; apply_state_commit(transaction, commit); } @@ -1774,23 +1808,21 @@ impl Backend { TransactionStorageMode::BlockBody => {}, TransactionStorageMode::StorageChain => { match Vec::::decode(&mut &body[..]) { - Ok(body) => { + Ok(body) => for ExtrinsicHeader { indexed_hash, .. } in body { if indexed_hash != Default::default() { - transaction.release( - columns::TRANSACTION, - indexed_hash, - ); + transaction.release(columns::TRANSACTION, indexed_hash); } - } - } - Err(err) => return Err(sp_blockchain::Error::Backend( - format!("Error decoding body list: {}", err) - )), + }, + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding body list: {}", + err + ))), } - } + }, } - } + }, None => return Ok(()), } Ok(()) @@ -1800,22 +1832,20 @@ impl Backend { let root = EmptyStorage::::new().0; // Empty trie let db_state = DbState::::new(self.storage.clone(), root); let state = RefTrackingState::new(db_state, self.storage.clone(), None); - let caching_state = CachingState::new( - state, - self.shared_cache.clone(), - None, - ); + let caching_state = CachingState::new(state, self.shared_cache.clone(), None); Ok(SyncingCachingState::new( - caching_state, - self.state_usage.clone(), - self.blockchain.meta.clone(), - self.import_lock.clone(), + caching_state, + self.state_usage.clone(), + self.blockchain.meta.clone(), + self.import_lock.clone(), )) } } - -fn apply_state_commit(transaction: &mut Transaction, commit: sc_state_db::CommitSet>) { +fn apply_state_commit( + transaction: &mut Transaction, + commit: sc_state_db::CommitSet>, +) { for (key, val) in commit.data.inserted.into_iter() { transaction.set_from_vec(columns::STATE, &key[..], val); } @@ -1842,10 +1872,10 @@ fn apply_index_ops( match op { IndexOperation::Insert { extrinsic, hash, size } => { index_map.insert(extrinsic, (hash, size)); - } + }, IndexOperation::Renew { extrinsic, hash } => { renewed_map.insert(extrinsic, DbHash::from_slice(hash.as_ref())); - } + }, } } for (index, extrinsic) in body.into_iter().enumerate() { @@ -1853,10 +1883,7 @@ fn apply_index_ops( let extrinsic_header = if let Some(hash) = renewed_map.get(&(index as u32)) { // Bump ref counter transaction.reference(columns::TRANSACTION, DbHash::from_slice(hash.as_ref())); - ExtrinsicHeader { - indexed_hash: hash.clone(), - data: extrinsic, - } + ExtrinsicHeader { indexed_hash: hash.clone(), data: extrinsic } } else { match index_map.get(&(index as u32)) { Some((hash, size)) if *size as usize <= extrinsic.len() => { @@ -1871,12 +1898,7 @@ fn apply_index_ops( data: extrinsic[..offset].to_vec(), } }, - _ => { - ExtrinsicHeader { - indexed_hash: Default::default(), - data: extrinsic, - } - } + _ => ExtrinsicHeader { indexed_hash: Default::default(), data: extrinsic }, } }; extrinsic_headers.push(extrinsic_header); @@ -1890,28 +1912,28 @@ fn apply_index_ops( extrinsic_headers.encode() } -fn apply_indexed_body( - transaction: &mut Transaction, - body: Vec>, -) { +fn apply_indexed_body(transaction: &mut Transaction, body: Vec>) { for extrinsic in body { let hash = sp_runtime::traits::BlakeTwo256::hash(&extrinsic); - transaction.store( - columns::TRANSACTION, - DbHash::from_slice(hash.as_ref()), - extrinsic, - ); + transaction.store(columns::TRANSACTION, DbHash::from_slice(hash.as_ref()), extrinsic); } } -impl sc_client_api::backend::AuxStore for Backend where Block: BlockT { +impl sc_client_api::backend::AuxStore for Backend +where + Block: BlockT, +{ fn insert_aux< 'a, 'b: 'a, 'c: 'a, - I: IntoIterator, - D: IntoIterator, - >(&self, insert: I, delete: D) -> ClientResult<()> { + I: IntoIterator, + D: IntoIterator, + >( + &self, + insert: I, + delete: D, + ) -> ClientResult<()> { let mut transaction = Transaction::new(); for (k, v) in insert { transaction.set(columns::AUX, k, v); @@ -1972,10 +1994,7 @@ impl sc_client_api::backend::Backend for Backend { Ok(()) } - fn commit_operation( - &self, - operation: Self::BlockImportOperation, - ) -> ClientResult<()> { + fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> { let usage = operation.old_state.usage_info(); self.state_usage.merge_sm(usage); @@ -1987,7 +2006,7 @@ impl sc_client_api::backend::Backend for Backend { e @ Err(_) => { self.storage.state_db.revert_pending(); e - } + }, } } @@ -2032,23 +2051,22 @@ impl sc_client_api::backend::Backend for Backend { let last_finalized = self.blockchain.last_finalized()?; // We can do a quick check first, before doing a proper but more expensive check - if number > self.blockchain.info().finalized_number - || (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?) + if number > self.blockchain.info().finalized_number || + (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?) { - return Err(ClientError::NotInFinalizedChain); + return Err(ClientError::NotInFinalizedChain) } - let justifications = - if let Some(mut stored_justifications) = self.blockchain.justifications(block)? { - if !stored_justifications.append(justification) { - return Err(ClientError::BadJustification( - "Duplicate consensus engine ID".into() - )); - } - stored_justifications - } else { - Justifications::from(justification) - }; + let justifications = if let Some(mut stored_justifications) = + self.blockchain.justifications(block)? + { + if !stored_justifications.append(justification) { + return Err(ClientError::BadJustification("Duplicate consensus engine ID".into())) + } + stored_justifications + } else { + Justifications::from(justification) + }; transaction.set_from_vec( columns::JUSTIFICATIONS, @@ -2070,25 +2088,20 @@ impl sc_client_api::backend::Backend for Backend { } fn usage_info(&self) -> Option { - let (io_stats, state_stats) = self.io_stats.take_or_else(|| + let (io_stats, state_stats) = self.io_stats.take_or_else(|| { ( // TODO: implement DB stats and cache size retrieval kvdb::IoStats::empty(), self.state_usage.take(), ) - ); + }); let database_cache = MemorySize::from_bytes(0); - let state_cache = MemorySize::from_bytes( - (*&self.shared_cache).read().used_storage_cache_size(), - ); + let state_cache = + MemorySize::from_bytes((*&self.shared_cache).read().used_storage_cache_size()); let state_db = self.storage.state_db.memory_info(); Some(UsageInfo { - memory: MemoryInfo { - state_cache, - database_cache, - state_db, - }, + memory: MemoryInfo { state_cache, database_cache, state_db }, io: IoInfo { transactions: io_stats.transactions, bytes_read: io_stats.bytes_read, @@ -2118,29 +2131,31 @@ impl sc_client_api::backend::Backend for Backend { let finalized = self.blockchain.info().finalized_number; let revertible = best_number - finalized; - let n = if !revert_finalized && revertible < n { - revertible - } else { - n - }; + let n = if !revert_finalized && revertible < n { revertible } else { n }; let mut revert_blocks = || -> ClientResult> { - for c in 0 .. n.saturated_into::() { + for c in 0..n.saturated_into::() { if best_number.is_zero() { return Ok(c.saturated_into::>()) } let mut transaction = Transaction::new(); let removed_number = best_number; - let removed = self.blockchain.header(BlockId::Number(best_number))?.ok_or_else( - || sp_blockchain::Error::UnknownBlock( - format!("Error reverting to {}. Block hash not found.", best_number)))?; + let removed = + self.blockchain.header(BlockId::Number(best_number))?.ok_or_else(|| { + sp_blockchain::Error::UnknownBlock(format!( + "Error reverting to {}. Block hash not found.", + best_number + )) + })?; let removed_hash = removed.hash(); let prev_number = best_number.saturating_sub(One::one()); - let prev_hash = self.blockchain.hash(prev_number)?.ok_or_else( - || sp_blockchain::Error::UnknownBlock( - format!("Error reverting to {}. Block hash not found.", best_number)) - )?; + let prev_hash = self.blockchain.hash(prev_number)?.ok_or_else(|| { + sp_blockchain::Error::UnknownBlock(format!( + "Error reverting to {}. Block hash not found.", + best_number + )) + })?; if !self.have_state_at(&prev_hash, prev_number) { return Ok(c.saturated_into::>()) @@ -2155,41 +2170,49 @@ impl sc_client_api::backend::Backend for Backend { let update_finalized = best_number < finalized; - let key = utils::number_and_hash_to_lookup_key(best_number.clone(), &best_hash)?; + let key = + utils::number_and_hash_to_lookup_key(best_number.clone(), &best_hash)?; let changes_trie_cache_ops = self.changes_tries_storage.revert( &mut transaction, - &cache::ComplexBlockId::new( - removed.hash(), - removed_number, - ), + &cache::ComplexBlockId::new(removed.hash(), removed_number), )?; if update_finalized { transaction.set_from_vec( columns::META, meta_keys::FINALIZED_BLOCK, - key.clone() + key.clone(), ); reverted_finalized.insert(removed_hash); if let Some((hash, _)) = self.blockchain.info().finalized_state { if hash == best_hash { - if !best_number.is_zero() - && self.have_state_at(&prev_hash, best_number - One::one()) + if !best_number.is_zero() && + self.have_state_at(&prev_hash, best_number - One::one()) { let lookup_key = utils::number_and_hash_to_lookup_key( best_number - One::one(), - prev_hash + prev_hash, )?; - transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key); + transaction.set_from_vec( + columns::META, + meta_keys::FINALIZED_STATE, + lookup_key, + ); } else { - transaction.remove(columns::META, meta_keys::FINALIZED_STATE); + transaction + .remove(columns::META, meta_keys::FINALIZED_STATE); } } } } transaction.set_from_vec(columns::META, meta_keys::BEST_BLOCK, key); transaction.remove(columns::KEY_LOOKUP, removed.hash().as_ref()); - children::remove_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, best_hash); + children::remove_children( + &mut transaction, + columns::META, + meta_keys::CHILDREN_PREFIX, + best_hash, + ); self.storage.db.commit(transaction)?; self.changes_tries_storage.post_commit(Some(changes_trie_cache_ops)); self.blockchain.update_meta(MetaUpdate { @@ -2197,10 +2220,10 @@ impl sc_client_api::backend::Backend for Backend { number: best_number, is_best: true, is_finalized: update_finalized, - with_state: false + with_state: false, }); - } - None => return Ok(c.saturated_into::>()) + }, + None => return Ok(c.saturated_into::>()), } } @@ -2225,36 +2248,27 @@ impl sc_client_api::backend::Backend for Backend { Ok((reverted, reverted_finalized)) } - fn remove_leaf_block( - &self, - hash: &Block::Hash, - ) -> ClientResult<()> { + fn remove_leaf_block(&self, hash: &Block::Hash) -> ClientResult<()> { let best_hash = self.blockchain.info().best_hash; if best_hash == *hash { - return Err( - sp_blockchain::Error::Backend( - format!("Can't remove best block {:?}", hash) - ) - ) + return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {:?}", hash))) } let hdr = self.blockchain.header_metadata(hash.clone())?; if !self.have_state_at(&hash, hdr.number) { - return Err( - sp_blockchain::Error::UnknownBlock( - format!("State already discarded for {:?}", hash) - ) - ) + return Err(sp_blockchain::Error::UnknownBlock(format!( + "State already discarded for {:?}", + hash + ))) } let mut leaves = self.blockchain.leaves.write(); if !leaves.contains(hdr.number, *hash) { - return Err( - sp_blockchain::Error::Backend( - format!("Can't remove non-leaf block {:?}", hash) - ) - ) + return Err(sp_blockchain::Error::Backend(format!( + "Can't remove non-leaf block {:?}", + hash + ))) } let mut transaction = Transaction::new(); @@ -2262,13 +2276,9 @@ impl sc_client_api::backend::Backend for Backend { apply_state_commit(&mut transaction, commit); } transaction.remove(columns::KEY_LOOKUP, hash.as_ref()); - let changes_trie_cache_ops = self.changes_tries_storage.revert( - &mut transaction, - &cache::ComplexBlockId::new( - *hash, - hdr.number, - ), - )?; + let changes_trie_cache_ops = self + .changes_tries_storage + .revert(&mut transaction, &cache::ComplexBlockId::new(*hash, hdr.number))?; self.changes_tries_storage.post_commit(Some(changes_trie_cache_ops)); leaves.revert(hash.clone(), hdr.number); @@ -2295,11 +2305,7 @@ impl sc_client_api::backend::Backend for Backend { let root = genesis_state.root.clone(); let db_state = DbState::::new(genesis_state.clone(), root); let state = RefTrackingState::new(db_state, self.storage.clone(), None); - let caching_state = CachingState::new( - state, - self.shared_cache.clone(), - None, - ); + let caching_state = CachingState::new(state, self.shared_cache.clone(), None); let mut state = SyncingCachingState::new( caching_state, self.state_usage.clone(), @@ -2313,33 +2319,26 @@ impl sc_client_api::backend::Backend for Backend { let hash = match block { BlockId::Hash(h) => h, - BlockId::Number(n) => self.blockchain.hash(n)?.ok_or_else(|| + BlockId::Number(n) => self.blockchain.hash(n)?.ok_or_else(|| { sp_blockchain::Error::UnknownBlock(format!("Unknown block number {}", n)) - )?, + })?, }; match self.blockchain.header_metadata(hash) { Ok(ref hdr) => { if !self.have_state_at(&hash, hdr.number) { - return Err( - sp_blockchain::Error::UnknownBlock( - format!("State already discarded for {:?}", block) - ) - ) + return Err(sp_blockchain::Error::UnknownBlock(format!( + "State already discarded for {:?}", + block + ))) } if let Ok(()) = self.storage.state_db.pin(&hash) { let root = hdr.state_root; let db_state = DbState::::new(self.storage.clone(), root); - let state = RefTrackingState::new( - db_state, - self.storage.clone(), - Some(hash.clone()), - ); - let caching_state = CachingState::new( - state, - self.shared_cache.clone(), - Some(hash), - ); + let state = + RefTrackingState::new(db_state, self.storage.clone(), Some(hash.clone())); + let caching_state = + CachingState::new(state, self.shared_cache.clone(), Some(hash)); Ok(SyncingCachingState::new( caching_state, self.state_usage.clone(), @@ -2347,11 +2346,10 @@ impl sc_client_api::backend::Backend for Backend { self.import_lock.clone(), )) } else { - Err( - sp_blockchain::Error::UnknownBlock( - format!("State already discarded for {:?}", block) - ) - ) + Err(sp_blockchain::Error::UnknownBlock(format!( + "State already discarded for {:?}", + block + ))) } }, Err(e) => Err(e), @@ -2361,13 +2359,13 @@ impl sc_client_api::backend::Backend for Backend { fn have_state_at(&self, hash: &Block::Hash, number: NumberFor) -> bool { if self.is_archive { match self.blockchain.header_metadata(hash.clone()) { - Ok(header) => { - sp_state_machine::Storage::get( - self.storage.as_ref(), - &header.state_root, - (&[], None), - ).unwrap_or(None).is_some() - }, + Ok(header) => sp_state_machine::Storage::get( + self.storage.as_ref(), + &header.state_root, + (&[], None), + ) + .unwrap_or(None) + .is_some(), _ => false, } } else { @@ -2384,18 +2382,22 @@ impl sc_client_api::backend::LocalBackend for Backend::default(); { - let mut trie = TrieDBMut::::new( - &mut changes_trie_update, - &mut changes_root - ); + let mut trie = + TrieDBMut::::new(&mut changes_trie_update, &mut changes_root); for (key, value) in changes { trie.insert(&key, &value).unwrap(); } @@ -2466,7 +2466,8 @@ pub(crate) mod tests { if let Some(index) = transaction_index { op.update_transaction_index(index).unwrap(); } - op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)).unwrap(); + op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)) + .unwrap(); backend.commit_operation(op).unwrap(); header_hash @@ -2500,13 +2501,8 @@ pub(crate) mod tests { extrinsics_root: Default::default(), }; - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); db.commit_operation(op).unwrap(); } @@ -2515,14 +2511,18 @@ pub(crate) mod tests { db.storage.db.clone() }; - let backend = Backend::::new(DatabaseSettings { - state_cache_size: 16777216, - state_cache_child_ratio: Some((50, 100)), - state_pruning: PruningMode::keep_blocks(1), - source: DatabaseSettingsSrc::Custom(backing), - keep_blocks: KeepBlocks::All, - transaction_storage: TransactionStorageMode::BlockBody, - }, 0).unwrap(); + let backend = Backend::::new( + DatabaseSettings { + state_cache_size: 16777216, + state_cache_child_ratio: Some((50, 100)), + state_pruning: PruningMode::keep_blocks(1), + source: DatabaseSettingsSrc::Custom(backing), + keep_blocks: KeepBlocks::All, + transaction_storage: TransactionStorageMode::BlockBody, + }, + 0, + ) + .unwrap(); assert_eq!(backend.blockchain().info().best_number, 9); for i in 0..10 { assert!(backend.blockchain().hash(i).unwrap().is_some()) @@ -2542,28 +2542,22 @@ pub(crate) mod tests { extrinsics_root: Default::default(), }; - let storage = vec![ - (vec![1, 3, 5], vec![2, 4, 6]), - (vec![1, 2, 3], vec![9, 9, 9]), - ]; + let storage = vec![(vec![1, 3, 5], vec![2, 4, 6]), (vec![1, 2, 3], vec![9, 9, 9])]; - header.state_root = op.old_state.storage_root(storage - .iter() - .map(|(x, y)| (&x[..], Some(&y[..]))) - ).0.into(); + header.state_root = op + .old_state + .storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..])))) + .0 + .into(); let hash = header.hash(); op.reset_storage(Storage { top: storage.into_iter().collect(), children_default: Default::default(), - }).unwrap(); - op.set_block_data( - header.clone(), - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + }) + .unwrap(); + op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); db.commit_operation(op).unwrap(); @@ -2587,26 +2581,17 @@ pub(crate) mod tests { extrinsics_root: Default::default(), }; - let storage = vec![ - (vec![1, 3, 5], None), - (vec![5, 5, 5], Some(vec![4, 5, 6])), - ]; + let storage = vec![(vec![1, 3, 5], None), (vec![5, 5, 5], Some(vec![4, 5, 6]))]; - let (root, overlay) = op.old_state.storage_root( - storage.iter() - .map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))) - ); + let (root, overlay) = op + .old_state + .storage_root(storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))); op.update_db_storage(overlay).unwrap(); header.state_root = root.into(); op.update_storage(storage, Vec::new()).unwrap(); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); db.commit_operation(op).unwrap(); @@ -2626,7 +2611,9 @@ pub(crate) mod tests { let hash = { let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::Hash(Default::default())).unwrap(); + backend + .begin_state_operation(&mut op, BlockId::Hash(Default::default())) + .unwrap(); let mut header = Header { number: 0, parent_hash: Default::default(), @@ -2641,22 +2628,22 @@ pub(crate) mod tests { op.reset_storage(Storage { top: Default::default(), children_default: Default::default(), - }).unwrap(); + }) + .unwrap(); key = op.db_updates.insert(EMPTY_PREFIX, b"hello"); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); backend.commit_operation(op).unwrap(); - assert_eq!(backend.storage.db.get( - columns::STATE, - &sp_trie::prefixed_key::(&key, EMPTY_PREFIX) - ).unwrap(), &b"hello"[..]); + assert_eq!( + backend + .storage + .db + .get(columns::STATE, &sp_trie::prefixed_key::(&key, EMPTY_PREFIX)) + .unwrap(), + &b"hello"[..] + ); hash }; @@ -2673,28 +2660,27 @@ pub(crate) mod tests { let storage: Vec<(_, _)> = vec![]; - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); + header.state_root = op + .old_state + .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y)))) + .0 + .into(); let hash = header.hash(); op.db_updates.insert(EMPTY_PREFIX, b"hello"); op.db_updates.remove(&key, EMPTY_PREFIX); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); backend.commit_operation(op).unwrap(); - assert_eq!(backend.storage.db.get( - columns::STATE, - &sp_trie::prefixed_key::(&key, EMPTY_PREFIX) - ).unwrap(), &b"hello"[..]); + assert_eq!( + backend + .storage + .db + .get(columns::STATE, &sp_trie::prefixed_key::(&key, EMPTY_PREFIX)) + .unwrap(), + &b"hello"[..] + ); hash }; @@ -2711,28 +2697,24 @@ pub(crate) mod tests { let storage: Vec<(_, _)> = vec![]; - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); + header.state_root = op + .old_state + .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y)))) + .0 + .into(); let hash = header.hash(); op.db_updates.remove(&key, EMPTY_PREFIX); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); backend.commit_operation(op).unwrap(); - assert!(backend.storage.db.get( - columns::STATE, - &sp_trie::prefixed_key::(&key, EMPTY_PREFIX) - ).is_some()); + assert!(backend + .storage + .db + .get(columns::STATE, &sp_trie::prefixed_key::(&key, EMPTY_PREFIX)) + .is_some()); hash }; @@ -2749,34 +2731,31 @@ pub(crate) mod tests { let storage: Vec<(_, _)> = vec![]; - header.state_root = op.old_state.storage_root(storage - .iter() - .cloned() - .map(|(x, y)| (x, Some(y))) - ).0.into(); + header.state_root = op + .old_state + .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y)))) + .0 + .into(); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); backend.commit_operation(op).unwrap(); - assert!(backend.storage.db.get( - columns::STATE, - &sp_trie::prefixed_key::(&key, EMPTY_PREFIX) - ).is_none()); + assert!(backend + .storage + .db + .get(columns::STATE, &sp_trie::prefixed_key::(&key, EMPTY_PREFIX)) + .is_none()); } backend.finalize_block(BlockId::Number(1), None).unwrap(); backend.finalize_block(BlockId::Number(2), None).unwrap(); backend.finalize_block(BlockId::Number(3), None).unwrap(); - assert!(backend.storage.db.get( - columns::STATE, - &sp_trie::prefixed_key::(&key, EMPTY_PREFIX) - ).is_none()); + assert!(backend + .storage + .db + .get(columns::STATE, &sp_trie::prefixed_key::(&key, EMPTY_PREFIX)) + .is_none()); } #[test] @@ -2798,8 +2777,14 @@ pub(crate) mod tests { let tree_route = tree_route(blockchain, a3, b2).unwrap(); assert_eq!(tree_route.common_block().hash, block0); - assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::>(), vec![a3, a2, a1]); - assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::>(), vec![b1, b2]); + assert_eq!( + tree_route.retracted().iter().map(|r| r.hash).collect::>(), + vec![a3, a2, a1] + ); + assert_eq!( + tree_route.enacted().iter().map(|r| r.hash).collect::>(), + vec![b1, b2] + ); } { @@ -2807,14 +2792,20 @@ pub(crate) mod tests { assert_eq!(tree_route.common_block().hash, a1); assert!(tree_route.retracted().is_empty()); - assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::>(), vec![a2, a3]); + assert_eq!( + tree_route.enacted().iter().map(|r| r.hash).collect::>(), + vec![a2, a3] + ); } { let tree_route = tree_route(blockchain, a3, a1).unwrap(); assert_eq!(tree_route.common_block().hash, a1); - assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::>(), vec![a3, a2]); + assert_eq!( + tree_route.retracted().iter().map(|r| r.hash).collect::>(), + vec![a3, a2] + ); assert!(tree_route.enacted().is_empty()); } @@ -2840,7 +2831,10 @@ pub(crate) mod tests { assert_eq!(tree_route.common_block().hash, block0); assert!(tree_route.retracted().is_empty()); - assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::>(), vec![block1]); + assert_eq!( + tree_route.enacted().iter().map(|r| r.hash).collect::>(), + vec![block1] + ); } } @@ -2938,20 +2932,25 @@ pub(crate) mod tests { #[test] fn test_leaves_with_complex_block_tree() { - let backend: Arc> = Arc::new(Backend::new_test(20, 20)); + let backend: Arc> = + Arc::new(Backend::new_test(20, 20)); substrate_test_runtime_client::trait_tests::test_leaves_for_backend(backend); } #[test] fn test_children_with_complex_block_tree() { - let backend: Arc> = Arc::new(Backend::new_test(20, 20)); + let backend: Arc> = + Arc::new(Backend::new_test(20, 20)); substrate_test_runtime_client::trait_tests::test_children_for_backend(backend); } #[test] fn test_blockchain_query_by_number_gets_canonical() { - let backend: Arc> = Arc::new(Backend::new_test(20, 20)); - substrate_test_runtime_client::trait_tests::test_blockchain_query_by_number_gets_canonical(backend); + let backend: Arc> = + Arc::new(Backend::new_test(20, 20)); + substrate_test_runtime_client::trait_tests::test_blockchain_query_by_number_gets_canonical( + backend, + ); } #[test] @@ -2969,7 +2968,10 @@ pub(crate) mod tests { let block2_b = insert_header(&backend, 2, block1_b, None, Default::default()); let block2_c = insert_header(&backend, 2, block1_b, None, [1; 32].into()); - assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a, block2_b, block2_c, block1_c]); + assert_eq!( + backend.blockchain().leaves().unwrap(), + vec![block2_a, block2_b, block2_c, block1_c] + ); backend.finalize_block(BlockId::hash(block1_a), None).unwrap(); backend.finalize_block(BlockId::hash(block2_a), None).unwrap(); @@ -2980,7 +2982,8 @@ pub(crate) mod tests { #[test] fn test_aux() { - let backend: Backend = Backend::new_test(0, 0); + let backend: Backend = + Backend::new_test(0, 0); assert!(backend.get_aux(b"test").unwrap().is_none()); backend.insert_aux(&[(&b"test"[..], &b"hello"[..])], &[]).unwrap(); assert_eq!(b"hello", &backend.get_aux(b"test").unwrap().unwrap()[..]); @@ -2990,7 +2993,7 @@ pub(crate) mod tests { #[test] fn test_finalize_block_with_justification() { - use sc_client_api::blockchain::{Backend as BlockChainBackend}; + use sc_client_api::blockchain::Backend as BlockChainBackend; let backend = Backend::::new_test(10, 10); @@ -3008,7 +3011,7 @@ pub(crate) mod tests { #[test] fn test_append_justification_to_finalized_block() { - use sc_client_api::blockchain::{Backend as BlockChainBackend}; + use sc_client_api::blockchain::Backend as BlockChainBackend; let backend = Backend::::new_test(10, 10); @@ -3016,10 +3019,7 @@ pub(crate) mod tests { let _ = insert_header(&backend, 1, block0, None, Default::default()); let just0 = (CONS0_ENGINE_ID, vec![1, 2, 3]); - backend.finalize_block( - BlockId::Number(1), - Some(just0.clone().into()), - ).unwrap(); + backend.finalize_block(BlockId::Number(1), Some(just0.clone().into())).unwrap(); let just1 = (CONS1_ENGINE_ID, vec![4, 5]); backend.append_justification(BlockId::Number(1), just1.clone()).unwrap(); @@ -3072,7 +3072,9 @@ pub(crate) mod tests { let hash0 = { let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::Hash(Default::default())).unwrap(); + backend + .begin_state_operation(&mut op, BlockId::Hash(Default::default())) + .unwrap(); let mut header = Header { number: 0, parent_hash: Default::default(), @@ -3083,30 +3085,28 @@ pub(crate) mod tests { let storage = vec![(b"test".to_vec(), b"test".to_vec())]; - header.state_root = op.old_state.storage_root(storage - .iter() - .map(|(x, y)| (&x[..], Some(&y[..]))) - ).0.into(); + header.state_root = op + .old_state + .storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..])))) + .0 + .into(); let hash = header.hash(); op.reset_storage(Storage { top: storage.into_iter().collect(), children_default: Default::default(), - }).unwrap(); - op.set_block_data( - header.clone(), - Some(vec![]), - None, - None, - NewBlockState::Best, - ).unwrap(); + }) + .unwrap(); + op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best) + .unwrap(); backend.commit_operation(op).unwrap(); hash }; - let block0_hash = backend.state_at(BlockId::Hash(hash0)) + let block0_hash = backend + .state_at(BlockId::Hash(hash0)) .unwrap() .storage_hash(&b"test"[..]) .unwrap(); @@ -3124,22 +3124,16 @@ pub(crate) mod tests { let storage = vec![(b"test".to_vec(), Some(b"test2".to_vec()))]; - let (root, overlay) = op.old_state.storage_root( - storage.iter() - .map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))) - ); + let (root, overlay) = op + .old_state + .storage_root(storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))); op.update_db_storage(overlay).unwrap(); header.state_root = root.into(); let hash = header.hash(); op.update_storage(storage, Vec::new()).unwrap(); - op.set_block_data( - header, - Some(vec![]), - None, - None, - NewBlockState::Normal, - ).unwrap(); + op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Normal) + .unwrap(); backend.commit_operation(op).unwrap(); @@ -3154,7 +3148,8 @@ pub(crate) mod tests { backend.commit_operation(op).unwrap(); } - let block1_hash = backend.state_at(BlockId::Hash(hash1)) + let block1_hash = backend + .state_at(BlockId::Hash(hash1)) .unwrap() .storage_hash(&b"test"[..]) .unwrap(); @@ -3184,7 +3179,8 @@ pub(crate) mod tests { let backend = Backend::::new_test(10, 10); // insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created - let mut prev_hash = insert_header(&backend, 0, Default::default(), None, Default::default()); + let mut prev_hash = + insert_header(&backend, 0, Default::default(), None, Default::default()); let cht_size: u64 = cht::size(); for i in 1..1 + cht_size + cht_size + 1 { prev_hash = insert_header(&backend, i, prev_hash, None, Default::default()); @@ -3192,12 +3188,18 @@ pub(crate) mod tests { let blockchain = backend.blockchain(); - let cht_root_1 = blockchain.header_cht_root(cht_size, cht::start_number(cht_size, 0)) - .unwrap().unwrap(); - let cht_root_2 = blockchain.header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2) - .unwrap().unwrap(); - let cht_root_3 = blockchain.header_cht_root(cht_size, cht::end_number(cht_size, 0)) - .unwrap().unwrap(); + let cht_root_1 = blockchain + .header_cht_root(cht_size, cht::start_number(cht_size, 0)) + .unwrap() + .unwrap(); + let cht_root_2 = blockchain + .header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2) + .unwrap() + .unwrap(); + let cht_root_3 = blockchain + .header_cht_root(cht_size, cht::end_number(cht_size, 0)) + .unwrap() + .unwrap(); assert_eq!(cht_root_1, cht_root_2); assert_eq!(cht_root_2, cht_root_3); } @@ -3208,8 +3210,16 @@ pub(crate) mod tests { let backend = Backend::::new_test_with_tx_storage(2, 0, *storage); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); - for i in 0 .. 5 { - let hash = insert_block(&backend, i, prev_hash, None, Default::default(), vec![i.into()], None); + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ); blocks.push(hash); prev_hash = hash; } @@ -3217,7 +3227,7 @@ pub(crate) mod tests { { let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::Hash(blocks[4])).unwrap(); - for i in 1 .. 5 { + for i in 1..5 { op.mark_finalized(BlockId::Hash(blocks[i]), None).unwrap(); } backend.commit_operation(op).unwrap(); @@ -3233,15 +3243,20 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize_with_fork() { - let backend = Backend::::new_test_with_tx_storage( - 2, - 10, - TransactionStorageMode::StorageChain - ); + let backend = + Backend::::new_test_with_tx_storage(2, 10, TransactionStorageMode::StorageChain); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); - for i in 0 .. 5 { - let hash = insert_block(&backend, i, prev_hash, None, Default::default(), vec![i.into()], None); + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ); blocks.push(hash); prev_hash = hash; } @@ -3254,15 +3269,23 @@ pub(crate) mod tests { None, sp_core::H256::random(), vec![2.into()], - None + None, + ); + insert_block( + &backend, + 3, + fork_hash_root, + None, + H256::random(), + vec![3.into(), 11.into()], + None, ); - insert_block(&backend, 3, fork_hash_root, None, H256::random(), vec![3.into(), 11.into()], None); let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::Hash(blocks[4])).unwrap(); op.mark_head(BlockId::Hash(blocks[4])).unwrap(); backend.commit_operation(op).unwrap(); - for i in 1 .. 5 { + for i in 1..5 { let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::Hash(blocks[4])).unwrap(); op.mark_finalized(BlockId::Hash(blocks[i]), None).unwrap(); @@ -3279,16 +3302,13 @@ pub(crate) mod tests { #[test] fn renew_transaction_storage() { - let backend = Backend::::new_test_with_tx_storage( - 2, - 10, - TransactionStorageMode::StorageChain - ); + let backend = + Backend::::new_test_with_tx_storage(2, 10, TransactionStorageMode::StorageChain); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); let x1 = ExtrinsicWrapper::from(0u64).encode(); - let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); - for i in 0 .. 10 { + let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); + for i in 0..10 { let mut index = Vec::new(); if i == 0 { index.push(IndexOperation::Insert { @@ -3298,10 +3318,7 @@ pub(crate) mod tests { }); } else if i < 5 { // keep renewing 1st - index.push(IndexOperation::Renew { - extrinsic: 0, - hash: x1_hash.as_ref().to_vec(), - }); + index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() }); } // else stop renewing let hash = insert_block( &backend, @@ -3310,13 +3327,13 @@ pub(crate) mod tests { None, Default::default(), vec![i.into()], - Some(index) + Some(index), ); blocks.push(hash); prev_hash = hash; } - for i in 1 .. 10 { + for i in 1..10 { let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::Hash(blocks[4])).unwrap(); op.mark_finalized(BlockId::Hash(blocks[i]), None).unwrap(); @@ -3332,15 +3349,20 @@ pub(crate) mod tests { #[test] fn remove_leaf_block_works() { - let backend = Backend::::new_test_with_tx_storage( - 2, - 10, - TransactionStorageMode::StorageChain - ); + let backend = + Backend::::new_test_with_tx_storage(2, 10, TransactionStorageMode::StorageChain); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); - for i in 0 .. 2 { - let hash = insert_block(&backend, i, prev_hash, None, Default::default(), vec![i.into()], None); + for i in 0..2 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ); blocks.push(hash); prev_hash = hash; } @@ -3353,7 +3375,7 @@ pub(crate) mod tests { None, sp_core::H256::random(), vec![42.into()], - None + None, ); assert!(backend.remove_leaf_block(&best_hash).is_err()); assert!(backend.have_state_at(&prev_hash, 1)); diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index 2efcd18732309..39b0941fd22c5 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -1087,7 +1087,7 @@ where // random between `[0, 2 * gossip_duration]` seconds. let delay: u64 = - thread_rng().gen_range(0 .. 2 * self.config.gossip_duration.as_millis() as u64); + thread_rng().gen_range(0..2 * self.config.gossip_duration.as_millis() as u64); Box::pin(Delay::new(Duration::from_millis(delay)).map(Ok)) } diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 4a4215ab80c6f..bc416d4c5ea7d 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -19,7 +19,7 @@ use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use log::debug; -use parity_scale_codec::{Encode, Decode}; +use parity_scale_codec::{Decode, Encode}; use sc_client_api::{backend::Backend, utils::is_descendent_of}; use sc_consensus::shared_data::{SharedDataLocked, SharedDataLockedUpgradable}; @@ -30,20 +30,21 @@ use sp_consensus::{ BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, Error as ConsensusError, ImportResult, JustificationImport, SelectChain, StateAction, }; -use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GrandpaApi, GRANDPA_ENGINE_ID}; -use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; -use sp_runtime::traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero}; -use sp_runtime::Justification; -use sp_utils::mpsc::TracingUnboundedSender; use sp_core::hashing::twox_128; +use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; +use sp_runtime::{ + generic::{BlockId, OpaqueDigestItemId}, + traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero}, + Justification, +}; +use sp_utils::mpsc::TracingUnboundedSender; use crate::{ authorities::{AuthoritySet, DelayKind, PendingChange, SharedAuthoritySet}, environment::finalize_block, justification::GrandpaJustification, notification::GrandpaJustificationSender, - ClientForGrandpa, CommandOrError, Error, NewAuthoritySet, VoterCommand, - AuthoritySetChanges, + AuthoritySetChanges, ClientForGrandpa, CommandOrError, Error, NewAuthoritySet, VoterCommand, }; /// A block-import handler for GRANDPA. @@ -100,12 +101,8 @@ where let chain_info = self.inner.info(); // request justifications for all pending changes for which change blocks have already been imported - let pending_changes: Vec<_> = self - .authority_set - .inner() - .pending_changes() - .cloned() - .collect(); + let pending_changes: Vec<_> = + self.authority_set.inner().pending_changes().cloned().collect(); for pending_change in pending_changes { if pending_change.delay_kind == DelayKind::Finalized && @@ -247,7 +244,7 @@ where ) -> Option>> { // check for forced authority set hard forks if let Some(change) = self.authority_set_hard_forks.get(&hash) { - return Some(change.clone()); + return Some(change.clone()) } // check for forced change. @@ -258,7 +255,7 @@ where canon_height: *header.number(), canon_hash: hash, delay_kind: DelayKind::Best { median_last_finalized }, - }); + }) } // check normal scheduled change. @@ -301,10 +298,9 @@ where fn consume( mut self, ) -> Option<(AuthoritySet, SharedDataLocked<'a, AuthoritySet>)> { - self.old.take().map(|old| ( - old, - self.guard.take().expect("only taken on deconstruction; qed"), - )) + self.old + .take() + .map(|old| (old, self.guard.take().expect("only taken on deconstruction; qed"))) } } @@ -317,20 +313,14 @@ where } let number = *(block.header.number()); - let maybe_change = self.check_new_change( - &block.header, - hash, - ); + let maybe_change = self.check_new_change(&block.header, hash); // returns a function for checking whether a block is a descendent of another // consistent with querying client directly after importing the block. let parent_hash = *block.header.parent_hash(); let is_descendent_of = is_descendent_of(&*self.inner, Some((hash, parent_hash))); - let mut guard = InnerGuard { - guard: Some(self.authority_set.inner_locked()), - old: None, - }; + let mut guard = InnerGuard { guard: Some(self.authority_set.inner_locked()), old: None }; // whether to pause the old authority set -- happens after import // of a forced change block. @@ -345,10 +335,10 @@ where do_pause = true; } - guard.as_mut().add_pending_change( - change, - &is_descendent_of, - ).map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + guard + .as_mut() + .add_pending_change(change, &is_descendent_of) + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; } let applied_changes = { @@ -395,7 +385,9 @@ where AppliedChanges::Forced(new_authorities) } else { - let did_standard = guard.as_mut().enacts_standard_change(hash, number, &is_descendent_of) + let did_standard = guard + .as_mut() + .enacts_standard_change(hash, number, &is_descendent_of) .map_err(|e| ConsensusError::ClientImport(e.to_string())) .map_err(ConsensusError::from)?; @@ -419,19 +411,17 @@ where crate::aux_schema::update_authority_set::( authorities, authorities_change, - |insert| block.auxiliary.extend( - insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) - ) + |insert| { + block + .auxiliary + .extend(insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))) + }, ); } let just_in_case = just_in_case.map(|(o, i)| (o, i.release_mutex())); - Ok(PendingSetChanges { - just_in_case, - applied_changes, - do_pause, - }) + Ok(PendingSetChanges { just_in_case, applied_changes, do_pause }) } /// Read current set id form a given state. @@ -443,14 +433,16 @@ where // This code may be removed once warp sync to an old runtime is no longer needed. for prefix in ["GrandpaFinality", "Grandpa"] { let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat(); - if let Ok(Some(id)) = self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec())) { + if let Ok(Some(id)) = + self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec())) + { if let Ok(id) = SetId::decode(&mut id.0.as_ref()) { return Ok(id) } } } Err(ConsensusError::ClientImport("Unable to read retrieve current set id.".into())) - } + }, Err(e) => Err(ConsensusError::ClientImport(e.to_string())), } } @@ -472,7 +464,10 @@ where // is correct and final. So we can read the authority list and set id from the state. self.authority_set_hard_forks.clear(); let block_id = BlockId::hash(hash); - let authorities = self.inner.runtime_api().grandpa_authorities(&block_id) + let authorities = self + .inner + .runtime_api() + .grandpa_authorities(&block_id) .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; let set_id = self.current_set_id(&block_id)?; let authority_set = AuthoritySet::new( @@ -481,23 +476,23 @@ where fork_tree::ForkTree::new(), Vec::new(), AuthoritySetChanges::empty(), - ).ok_or_else(|| ConsensusError::ClientImport("Invalid authority list".into()))?; + ) + .ok_or_else(|| ConsensusError::ClientImport("Invalid authority list".into()))?; *self.authority_set.inner_locked() = authority_set.clone(); crate::aux_schema::update_authority_set::( &authority_set, None, - |insert| self.inner.insert_aux(insert, []) - ).map_err(|e| ConsensusError::ClientImport(e.to_string()))?; - let new_set = NewAuthoritySet { - canon_number: number, - canon_hash: hash, - set_id, - authorities, - }; - let _ = self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new_set)); + |insert| self.inner.insert_aux(insert, []), + ) + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; + let new_set = + NewAuthoritySet { canon_number: number, canon_hash: hash, set_id, authorities }; + let _ = self + .send_voter_commands + .unbounded_send(VoterCommand::ChangeAuthorities(new_set)); Ok(ImportResult::Imported(aux)) - } + }, Ok(r) => Ok(r), Err(e) => Err(ConsensusError::ClientImport(e.to_string())), } @@ -535,13 +530,16 @@ where // Strip justifications when re-importing an existing block. let _justifications = block.justifications.take(); return (&*self.inner).import_block(block, new_cache).await - } + }, Ok(BlockStatus::Unknown) => {}, Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } - if matches!(block.state_action, StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_))) { - return self.import_state(block, new_cache).await; + if matches!( + block.state_action, + StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_)) + ) { + return self.import_state(block, new_cache).await } // on initial sync we will restrict logging under info to avoid spam. @@ -563,7 +561,7 @@ where r, ); pending_changes.revert(); - return Ok(r); + return Ok(r) }, Err(e) => { debug!( @@ -572,7 +570,7 @@ where e, ); pending_changes.revert(); - return Err(ConsensusError::ClientImport(e.to_string())); + return Err(ConsensusError::ClientImport(e.to_string())) }, } }; @@ -581,9 +579,9 @@ where // Send the pause signal after import but BEFORE sending a `ChangeAuthorities` message. if do_pause { - let _ = self.send_voter_commands.unbounded_send( - VoterCommand::Pause("Forced change scheduled after inactivity".to_string()) - ); + let _ = self.send_voter_commands.unbounded_send(VoterCommand::Pause( + "Forced change scheduled after inactivity".to_string(), + )); } let needs_justification = applied_changes.needs_justification(); @@ -601,7 +599,8 @@ where // they should import the block and discard the justification, and they will // then request a justification from sync if it's necessary (which they should // then be able to successfully validate). - let _ = self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new)); + let _ = + self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new)); // we must clear all pending justifications requests, presumably they won't be // finalized hence why this forced changes was triggered @@ -617,8 +616,8 @@ where _ => {}, } - let grandpa_justification = justifications - .and_then(|just| just.into_justification(GRANDPA_ENGINE_ID)); + let grandpa_justification = + justifications.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID)); match grandpa_justification { Some(justification) => { @@ -639,7 +638,7 @@ where } }); }, - None => { + None => if needs_justification { debug!( target: "afg", @@ -648,8 +647,7 @@ where ); imported_aux.needs_justification = true; - } - } + }, } Ok(ImportResult::Imported(imported_aux)) @@ -696,14 +694,9 @@ impl GrandpaBlockImport { - afg_log!(initial_sync, + afg_log!( + initial_sync, "👴 Imported justification for block #{} that triggers \ command {}, signaling voter.", number, @@ -793,10 +787,13 @@ where Error::Signing(error) => ConsensusError::ClientImport(error), Error::Timer(error) => ConsensusError::ClientImport(error.to_string()), Error::RuntimeApi(error) => ConsensusError::ClientImport(error.to_string()), - }); + }) }, Ok(_) => { - assert!(!enacts_change, "returns Ok when no authority set change should be enacted; qed;"); + assert!( + !enacts_change, + "returns Ok when no authority set change should be enacted; qed;" + ); }, } diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 737d0d867f5bb..237fccb5ef242 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -56,41 +56,39 @@ #![warn(missing_docs)] -use futures::{ - prelude::*, - StreamExt, -}; +use futures::{prelude::*, StreamExt}; use log::{debug, error, info}; +use parity_scale_codec::{Decode, Encode}; +use parking_lot::RwLock; +use prometheus_endpoint::{PrometheusError, Registry}; use sc_client_api::{ backend::{AuxStore, Backend}, - LockImportRun, BlockchainEvents, CallExecutor, StorageProvider, - ExecutionStrategy, Finalizer, TransactionFor, ExecutorProvider, + BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun, + StorageProvider, TransactionFor, }; -use parity_scale_codec::{Decode, Encode}; -use prometheus_endpoint::{PrometheusError, Registry}; +use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sp_api::ProvideRuntimeApi; -use sp_blockchain::{HeaderBackend, Error as ClientError, HeaderMetadata}; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::{NumberFor, Block as BlockT, DigestFor, Zero}; -use sp_consensus::{SelectChain, BlockImport}; -use sp_core::{ - crypto::Public, -}; -use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore}; use sp_application_crypto::AppKey; +use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; +use sp_consensus::{BlockImport, SelectChain}; +use sp_core::crypto::Public; +use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use sp_runtime::{ + generic::BlockId, + traits::{Block as BlockT, DigestFor, NumberFor, Zero}, +}; use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; -use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO, CONSENSUS_DEBUG}; -use parking_lot::RwLock; -use finality_grandpa::Error as GrandpaError; -use finality_grandpa::{voter, voter_set::VoterSet}; pub use finality_grandpa::BlockNumberOps; - -use std::{fmt, io}; -use std::sync::Arc; -use std::time::Duration; -use std::pin::Pin; -use std::task::{Poll, Context}; +use finality_grandpa::{voter, voter_set::VoterSet, Error as GrandpaError}; + +use std::{ + fmt, io, + pin::Pin, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; // utility logging macro that takes as first argument a conditional to // decide whether to log under debug or info level (useful to restrict @@ -124,6 +122,7 @@ pub mod warp_proof; pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet}; pub use aux_schema::best_justification; +pub use finality_grandpa::voter::report; pub use finality_proof::{FinalityProof, FinalityProofError, FinalityProofProvider}; pub use import::{find_forced_change, find_scheduled_change, GrandpaBlockImport}; pub use justification::GrandpaJustification; @@ -133,13 +132,12 @@ pub use voting_rule::{ BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRuleResult, VotingRulesBuilder, }; -pub use finality_grandpa::voter::report; use aux_schema::PersistentData; use communication::{Network as NetworkT, NetworkBridge}; use environment::{Environment, VoterSetState}; -use until_imported::UntilGlobalMessageBlocksImported; use sp_finality_grandpa::{AuthorityList, AuthoritySignature, SetId}; +use until_imported::UntilGlobalMessageBlocksImported; // Re-export these two because it's just so damn convenient. pub use sp_finality_grandpa::{AuthorityId, AuthorityPair, GrandpaApi, ScheduledChange}; @@ -160,7 +158,8 @@ pub type SignedMessage = finality_grandpa::SignedMessage< >; /// A primary propose message for this chain's block type. -pub type PrimaryPropose = finality_grandpa::PrimaryPropose<::Hash, NumberFor>; +pub type PrimaryPropose = + finality_grandpa::PrimaryPropose<::Hash, NumberFor>; /// A prevote message for this chain's block type. pub type Prevote = finality_grandpa::Prevote<::Hash, NumberFor>; /// A precommit message for this chain's block type. @@ -199,22 +198,14 @@ type CommunicationIn = finality_grandpa::voter::CommunicationIn< /// Global communication input stream for commits and catch up messages, with /// the hash type not being derived from the block, useful for forcing the hash /// to some type (e.g. `H256`) when the compiler can't do the inference. -type CommunicationInH = finality_grandpa::voter::CommunicationIn< - H, - NumberFor, - AuthoritySignature, - AuthorityId, ->; +type CommunicationInH = + finality_grandpa::voter::CommunicationIn, AuthoritySignature, AuthorityId>; /// Global communication sink for commits with the hash type not being derived /// from the block, useful for forcing the hash to some type (e.g. `H256`) when /// the compiler can't do the inference. -type CommunicationOutH = finality_grandpa::voter::CommunicationOut< - H, - NumberFor, - AuthoritySignature, - AuthorityId, ->; +type CommunicationOutH = + finality_grandpa::voter::CommunicationOut, AuthoritySignature, AuthorityId>; /// Shared voter state for querying. pub struct SharedVoterState { @@ -224,18 +215,14 @@ pub struct SharedVoterState { impl SharedVoterState { /// Create a new empty `SharedVoterState` instance. pub fn empty() -> Self { - Self { - inner: Arc::new(RwLock::new(None)), - } + Self { inner: Arc::new(RwLock::new(None)) } } fn reset( &self, voter_state: Box + Sync + Send>, ) -> Option<()> { - let mut shared_voter_state = self - .inner - .try_write_for(Duration::from_secs(1))?; + let mut shared_voter_state = self.inner.try_write_for(Duration::from_secs(1))?; *shared_voter_state = Some(voter_state); Some(()) @@ -324,7 +311,8 @@ pub(crate) trait BlockStatus { fn block_number(&self, hash: Block::Hash) -> Result>, Error>; } -impl BlockStatus for Arc where +impl BlockStatus for Arc +where Client: HeaderBackend, NumberFor: BlockNumberOps, { @@ -338,26 +326,38 @@ impl BlockStatus for Arc where /// Ideally this would be a trait alias, we're not there yet. /// tracking issue pub trait ClientForGrandpa: - LockImportRun + Finalizer + AuxStore - + HeaderMetadata + HeaderBackend - + BlockchainEvents + ProvideRuntimeApi + ExecutorProvider + LockImportRun + + Finalizer + + AuxStore + + HeaderMetadata + + HeaderBackend + + BlockchainEvents + + ProvideRuntimeApi + + ExecutorProvider + BlockImport, Error = sp_consensus::Error> + StorageProvider - where - BE: Backend, - Block: BlockT, -{} +where + BE: Backend, + Block: BlockT, +{ +} impl ClientForGrandpa for T - where - BE: Backend, - Block: BlockT, - T: LockImportRun + Finalizer + AuxStore - + HeaderMetadata + HeaderBackend - + BlockchainEvents + ProvideRuntimeApi + ExecutorProvider - + BlockImport, Error = sp_consensus::Error> - + StorageProvider, -{} +where + BE: Backend, + Block: BlockT, + T: LockImportRun + + Finalizer + + AuxStore + + HeaderMetadata + + HeaderBackend + + BlockchainEvents + + ProvideRuntimeApi + + ExecutorProvider + + BlockImport, Error = sp_consensus::Error> + + StorageProvider, +{ +} /// Something that one can ask to do a block sync request. pub(crate) trait BlockSyncRequester { @@ -367,14 +367,25 @@ pub(crate) trait BlockSyncRequester { /// If the given vector of peers is empty then the underlying implementation /// should make a best effort to fetch the block from any peers it is /// connected to (NOTE: this assumption will change in the future #3629). - fn set_sync_fork_request(&self, peers: Vec, hash: Block::Hash, number: NumberFor); + fn set_sync_fork_request( + &self, + peers: Vec, + hash: Block::Hash, + number: NumberFor, + ); } -impl BlockSyncRequester for NetworkBridge where +impl BlockSyncRequester for NetworkBridge +where Block: BlockT, Network: NetworkT, { - fn set_sync_fork_request(&self, peers: Vec, hash: Block::Hash, number: NumberFor) { + fn set_sync_fork_request( + &self, + peers: Vec, + hash: Block::Hash, + number: NumberFor, + ) { NetworkBridge::set_sync_fork_request(self, peers, hash, number) } } @@ -394,7 +405,7 @@ pub(crate) enum VoterCommand { /// Pause the voter for given reason. Pause(String), /// New authorities. - ChangeAuthorities(NewAuthoritySet) + ChangeAuthorities(NewAuthoritySet), } impl fmt::Display for VoterCommand { @@ -439,7 +450,7 @@ impl From> for CommandOrError { } } -impl ::std::error::Error for CommandOrError { } +impl ::std::error::Error for CommandOrError {} impl fmt::Display for CommandOrError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -479,8 +490,10 @@ pub trait GenesisAuthoritySetProvider { fn get(&self) -> Result; } -impl GenesisAuthoritySetProvider for Arc> - where E: CallExecutor, +impl GenesisAuthoritySetProvider + for Arc> +where + E: CallExecutor, { fn get(&self) -> Result { // This implementation uses the Grandpa runtime API instead of reading directly from the @@ -495,10 +508,12 @@ impl GenesisAuthoritySetProvider for Arc( genesis_authorities_provider: &dyn GenesisAuthoritySetProvider, select_chain: SC, telemetry: Option, -) -> Result< - ( - GrandpaBlockImport, - LinkHalf, - ), - ClientError, -> +) -> Result<(GrandpaBlockImport, LinkHalf), ClientError> where SC: SelectChain, BE: Backend + 'static, @@ -542,13 +551,7 @@ pub fn block_import_with_authority_set_hard_forks select_chain: SC, authority_set_hard_forks: Vec<(SetId, (Block::Hash, NumberFor), AuthorityList)>, telemetry: Option, -) -> Result< - ( - GrandpaBlockImport, - LinkHalf, - ), - ClientError, -> +) -> Result<(GrandpaBlockImport, LinkHalf), ClientError> where SC: SelectChain, BE: Backend + 'static, @@ -557,11 +560,8 @@ where let chain_info = client.info(); let genesis_hash = chain_info.genesis_hash; - let persistent_data = aux_schema::load_persistent( - &*client, - genesis_hash, - >::zero(), - { + let persistent_data = + aux_schema::load_persistent(&*client, genesis_hash, >::zero(), { let telemetry = telemetry.clone(); move || { let authorities = genesis_authorities_provider.get()?; @@ -573,13 +573,11 @@ where ); Ok(authorities) } - }, - )?; + })?; let (voter_commands_tx, voter_commands_rx) = tracing_unbounded("mpsc_grandpa_voter_command"); - let (justification_sender, justification_stream) = - GrandpaJustificationStream::channel(); + 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. @@ -649,11 +647,8 @@ where let is_voter = local_authority_id(voters, keystore).is_some(); // verification stream - let (global_in, global_out) = network.global_communication( - communication::SetId(set_id), - voters.clone(), - is_voter, - ); + let (global_in, global_out) = + network.global_communication(communication::SetId(set_id), voters.clone(), is_voter); // block commit and catch up messages until relevant blocks are imported. let global_in = UntilGlobalMessageBlocksImported::new( @@ -761,23 +756,18 @@ where ); let conf = config.clone(); - let telemetry_task = if let Some(telemetry_on_connect) = telemetry - .as_ref() - .map(|x| x.on_connect_stream()) - { - let authorities = persistent_data.authority_set.clone(); - let telemetry = telemetry.clone(); - let events = telemetry_on_connect - .for_each(move |_| { + let telemetry_task = + if let Some(telemetry_on_connect) = telemetry.as_ref().map(|x| x.on_connect_stream()) { + let authorities = persistent_data.authority_set.clone(); + let telemetry = telemetry.clone(); + let events = telemetry_on_connect.for_each(move |_| { let current_authorities = authorities.current_authorities(); let set_id = authorities.set_id(); let authority_id = local_authority_id(¤t_authorities, conf.keystore.as_ref()) .unwrap_or_default(); - let authorities = current_authorities - .iter() - .map(|(id, _)| id.to_string()) - .collect::>(); + let authorities = + current_authorities.iter().map(|(id, _)| id.to_string()).collect::>(); let authorities = serde_json::to_string(&authorities).expect( "authorities is always at least an empty vector; \ @@ -795,10 +785,10 @@ where future::ready(()) }); - future::Either::Left(events) - } else { - future::Either::Right(future::pending()) - }; + future::Either::Left(events) + } else { + future::Either::Right(future::pending()) + }; let voter_work = VoterWork::new( client, @@ -822,8 +812,7 @@ where }); // Make sure that `telemetry_task` doesn't accidentally finish and kill grandpa. - let telemetry_task = telemetry_task - .then(|_| future::pending::<()>()); + let telemetry_task = telemetry_task.then(|_| future::pending::<()>()); Ok(future::select(voter_work, telemetry_task).map(drop)) } @@ -845,7 +834,9 @@ impl Metrics { /// Future that powers the voter. #[must_use] struct VoterWork, SC, VR> { - voter: Pin>>> + Send>>, + voter: Pin< + Box>>> + Send>, + >, shared_voter_state: SharedVoterState, env: Arc>, voter_commands_rx: TracingUnboundedReceiver>>, @@ -884,7 +875,7 @@ where Some(Err(e)) => { debug!(target: "afg", "Failed to register metrics: {:?}", e); None - } + }, None => None, }; @@ -940,12 +931,7 @@ where let chain_info = self.env.client.info(); - let authorities = self - .env - .voters - .iter() - .map(|(id, _)| id.to_string()) - .collect::>(); + let authorities = self.env.voters.iter().map(|(id, _)| id.to_string()).collect::>(); let authorities = serde_json::to_string(&authorities).expect( "authorities is always at least an empty vector; elements are always of type string; qed.", @@ -964,10 +950,7 @@ where match &*self.env.voter_set_state.read() { VoterSetState::Live { completed_rounds, .. } => { - let last_finalized = ( - chain_info.finalized_hash, - chain_info.finalized_number, - ); + let last_finalized = (chain_info.finalized_hash, chain_info.finalized_number); let global_comms = global_communication( self.env.set_id, @@ -1000,20 +983,18 @@ where self.voter = Box::pin(voter); }, - VoterSetState::Paused { .. } => - self.voter = Box::pin(future::pending()), + VoterSetState::Paused { .. } => self.voter = Box::pin(future::pending()), }; } fn handle_voter_command( &mut self, - command: VoterCommand> + command: VoterCommand>, ) -> Result<(), Error> { match command { VoterCommand::ChangeAuthorities(new) => { - let voters: Vec = new.authorities.iter().map(move |(a, _)| { - format!("{}", a) - }).collect(); + let voters: Vec = + new.authorities.iter().map(move |(a, _)| format!("{}", a)).collect(); telemetry!( self.telemetry; CONSENSUS_INFO; @@ -1037,14 +1018,12 @@ where Ok(Some(set_state)) })?; - let voters = Arc::new(VoterSet::new(new.authorities.into_iter()) - .expect( - "new authorities come from pending change; \ + let voters = Arc::new(VoterSet::new(new.authorities.into_iter()).expect( + "new authorities come from pending change; \ pending change comes from `AuthoritySet`; \ `AuthoritySet` validates authorities is non-empty and weights are non-zero; \ - qed." - ) - ); + qed.", + )); self.env = Arc::new(Environment { voters, @@ -1064,7 +1043,7 @@ where self.rebuild_voter(); Ok(()) - } + }, VoterCommand::Pause(reason) => { info!(target: "afg", "Pausing old validator set: {}", reason); @@ -1079,7 +1058,7 @@ where self.rebuild_voter(); Ok(()) - } + }, } } } @@ -1099,37 +1078,35 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { match Future::poll(Pin::new(&mut self.voter), cx) { - Poll::Pending => {} + Poll::Pending => {}, Poll::Ready(Ok(())) => { // voters don't conclude naturally - return Poll::Ready( - Err(Error::Safety("finality-grandpa inner voter has concluded.".into())) - ) - } + return Poll::Ready(Err(Error::Safety( + "finality-grandpa inner voter has concluded.".into(), + ))) + }, Poll::Ready(Err(CommandOrError::Error(e))) => { // return inner observer error return Poll::Ready(Err(e)) - } + }, Poll::Ready(Err(CommandOrError::VoterCommand(command))) => { // some command issued internally self.handle_voter_command(command)?; cx.waker().wake_by_ref(); - } + }, } match Stream::poll_next(Pin::new(&mut self.voter_commands_rx), cx) { - Poll::Pending => {} + Poll::Pending => {}, Poll::Ready(None) => { // the `voter_commands_rx` stream should never conclude since it's never closed. - return Poll::Ready( - Err(Error::Safety("`voter_commands_rx` was closed.".into())) - ) - } + return Poll::Ready(Err(Error::Safety("`voter_commands_rx` was closed.".into()))) + }, Poll::Ready(Some(command)) => { // some command issued externally self.handle_voter_command(command)?; cx.waker().wake_by_ref(); - } + }, } Future::poll(Pin::new(&mut self.network), cx) @@ -1145,10 +1122,10 @@ fn local_authority_id( ) -> Option { keystore.and_then(|keystore| { voters - .iter() - .find(|(p, _)| { - SyncCryptoStore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)]) - }) - .map(|(p, _)| p.clone()) + .iter() + .find(|(p, _)| { + SyncCryptoStore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)]) + }) + .map(|(p, _)| p.clone()) }) } diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/finality-grandpa/src/warp_proof.rs index 9abb4728e37e0..ebb437a479722 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -14,25 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . - //! Utilities for generating and verifying GRANDPA warp sync proofs. -use sp_runtime::codec::{Decode, Encode, self}; +use sp_runtime::codec::{self, Decode, Encode}; -use sc_client_api::Backend as ClientBackend; use crate::{ - find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification, - best_justification, SharedAuthoritySet, + best_justification, find_scheduled_change, AuthoritySetChanges, BlockNumberOps, + GrandpaJustification, SharedAuthoritySet, }; +use sc_client_api::Backend as ClientBackend; +use sc_network::warp_request_handler::{EncodedProof, VerificationResult, WarpSyncProvider}; use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT, NumberFor, One}, }; -use sc_network::warp_request_handler::{ - WarpSyncProvider, EncodedProof, VerificationResult -}; use std::sync::Arc; @@ -96,9 +93,7 @@ impl WarpSyncProof { .ok_or_else(|| Error::InvalidRequest("Missing start block".to_string()))?; if begin_number > blockchain.info().finalized_number { - return Err(Error::InvalidRequest( - "Start block is not finalized".to_string(), - )); + return Err(Error::InvalidRequest("Start block is not finalized".to_string())) } let canon_hash = blockchain.hash(begin_number)?.expect( @@ -110,15 +105,14 @@ impl WarpSyncProof { if canon_hash != begin { return Err(Error::InvalidRequest( "Start block is not in the finalized chain".to_string(), - )); + )) } let mut proofs = Vec::new(); let mut proofs_encoded_len = 0; let mut proof_limit_reached = false; - let set_changes = set_changes.iter_from(begin_number) - .ok_or(Error::MissingData)?; + let set_changes = set_changes.iter_from(begin_number).ok_or(Error::MissingData)?; for (_, last_block) in set_changes { let header = blockchain.header(BlockId::Number(*last_block))?.expect( @@ -131,7 +125,7 @@ impl WarpSyncProof { // if it doesn't contain a signal for standard change then the set must have changed // through a forced changed, in which case we stop collecting proofs as the chain of // trust in authority handoffs was broken. - break; + break } let justification = blockchain @@ -145,10 +139,7 @@ impl WarpSyncProof { let justification = GrandpaJustification::::decode(&mut &justification[..])?; - let proof = WarpSyncFragment { - header: header.clone(), - justification, - }; + let proof = WarpSyncFragment { header: header.clone(), justification }; let proof_size = proof.encoded_size(); // Check for the limit. We remove some bytes from the maximum size, because we're only @@ -156,7 +147,7 @@ impl WarpSyncProof { // room for rest of the data (the size of the `Vec` and the boolean). if proofs_encoded_len + proof_size >= MAX_WARP_SYNC_PROOF_SIZE - 50 { proof_limit_reached = true; - break; + break } proofs_encoded_len += proof_size; @@ -166,37 +157,30 @@ impl WarpSyncProof { let is_finished = if proof_limit_reached { false } else { - let latest_justification = - best_justification(backend)?.filter(|justification| { - // the existing best justification must be for a block higher than the - // last authority set change. if we didn't prove any authority set - // change then we fallback to make sure it's higher or equal to the - // initial warp sync block. - let limit = proofs - .last() - .map(|proof| proof.justification.target().0 + One::one()) - .unwrap_or(begin_number); - - justification.target().0 >= limit - }); + let latest_justification = best_justification(backend)?.filter(|justification| { + // the existing best justification must be for a block higher than the + // last authority set change. if we didn't prove any authority set + // change then we fallback to make sure it's higher or equal to the + // initial warp sync block. + let limit = proofs + .last() + .map(|proof| proof.justification.target().0 + One::one()) + .unwrap_or(begin_number); + + justification.target().0 >= limit + }); if let Some(latest_justification) = latest_justification { let header = blockchain.header(BlockId::Hash(latest_justification.target().1))? .expect("header hash corresponds to a justification in db; must exist in db as well; qed."); - proofs.push(WarpSyncFragment { - header, - justification: latest_justification, - }) + proofs.push(WarpSyncFragment { header, justification: latest_justification }) } true }; - let final_outcome = WarpSyncProof { - proofs, - is_finished, - }; + let final_outcome = WarpSyncProof { proofs, is_finished }; debug_assert!(final_outcome.encoded_size() <= MAX_WARP_SYNC_PROOF_SIZE); Ok(final_outcome) } @@ -223,8 +207,8 @@ impl WarpSyncProof { if proof.justification.target().1 != proof.header.hash() { return Err(Error::InvalidProof( - "Mismatch between header and justification".to_owned() - )); + "Mismatch between header and justification".to_owned(), + )) } if let Some(scheduled_change) = find_scheduled_change::(&proof.header) { @@ -235,7 +219,7 @@ impl WarpSyncProof { // the authority set change. return Err(Error::InvalidProof( "Header is missing authority set change digest".to_string(), - )); + )) } } Ok((current_set_id, current_authorities)) @@ -258,16 +242,13 @@ where /// Create a new istance for a given backend and authority set. pub fn new( backend: Arc, - authority_set: SharedAuthoritySet> + authority_set: SharedAuthoritySet>, ) -> Self { - NetworkProvider { - backend, - authority_set, - } + NetworkProvider { backend, authority_set } } } -impl > WarpSyncProvider +impl> WarpSyncProvider for NetworkProvider where NumberFor: BlockNumberOps, @@ -277,27 +258,36 @@ where &*self.backend, start, &self.authority_set.authority_set_changes(), - ).map_err(|e| e.to_string())?; + ) + .map_err(|e| e.to_string())?; Ok(EncodedProof(proof.encode())) } - fn verify(&self, + fn verify( + &self, proof: &EncodedProof, set_id: SetId, authorities: AuthorityList, - ) -> Result, String> - { + ) -> Result, String> { let EncodedProof(proof) = proof; let proof = WarpSyncProof::::decode(&mut proof.as_slice()) .map_err(|e| format!("Proof decoding error: {:?}", e))?; - let last_header = proof.proofs.last().map(|p| p.header.clone()) + let last_header = proof + .proofs + .last() + .map(|p| p.header.clone()) .ok_or_else(|| "Empty proof".to_string())?; - let (next_set_id, next_authorities) = proof.verify(set_id, authorities) + let (next_set_id, next_authorities) = proof + .verify(set_id, authorities) .map_err(|e| format!("Proof verification error: {:?}", e))?; if proof.is_finished { Ok(VerificationResult::::Complete(next_set_id, next_authorities, last_header)) } else { - Ok(VerificationResult::::Partial(next_set_id, next_authorities, last_header.hash())) + Ok(VerificationResult::::Partial( + next_set_id, + next_authorities, + last_header.hash(), + )) } } @@ -308,8 +298,7 @@ where #[cfg(test)] mod tests { - use super::WarpSyncProof; - use super::codec::Encode; + use super::{codec::Encode, WarpSyncProof}; use crate::{AuthoritySetChanges, GrandpaJustification}; use rand::prelude::*; use sc_block_builder::BlockBuilderProvider; @@ -339,12 +328,7 @@ mod tests { let mut authority_set_changes = Vec::new(); for n in 1..=100 { - let mut block = client - .new_block(Default::default()) - .unwrap() - .build() - .unwrap() - .block; + let mut block = client.new_block(Default::default()).unwrap().build().unwrap().block; let mut new_authorities = None; @@ -367,10 +351,7 @@ mod tests { let digest = sp_runtime::generic::DigestItem::Consensus( sp_finality_grandpa::GRANDPA_ENGINE_ID, sp_finality_grandpa::ConsensusLog::ScheduledChange( - sp_finality_grandpa::ScheduledChange { - delay: 0u64, - next_authorities, - }, + sp_finality_grandpa::ScheduledChange { delay: 0u64, next_authorities }, ) .encode(), ); @@ -390,10 +371,7 @@ mod tests { let mut precommits = Vec::new(); for keyring in ¤t_authorities { - let precommit = finality_grandpa::Precommit { - target_hash, - target_number, - }; + let precommit = finality_grandpa::Precommit { target_hash, target_number }; let msg = finality_grandpa::Message::Precommit(precommit.clone()); let encoded = sp_finality_grandpa::localized_payload(42, current_set_id, &msg); @@ -408,18 +386,14 @@ mod tests { precommits.push(precommit); } - let commit = finality_grandpa::Commit { - target_hash, - target_number, - precommits, - }; + let commit = finality_grandpa::Commit { target_hash, target_number, precommits }; let justification = GrandpaJustification::from_commit(&client, 42, commit).unwrap(); client .finalize_block( BlockId::Hash(target_hash), - Some((GRANDPA_ENGINE_ID, justification.encode())) + Some((GRANDPA_ENGINE_ID, justification.encode())), ) .unwrap(); @@ -439,10 +413,7 @@ mod tests { WarpSyncProof::generate(&*backend, genesis_hash, &authority_set_changes).unwrap(); // verifying the proof should yield the last set id and authorities - let (new_set_id, new_authorities) = warp_sync_proof.verify( - 0, - genesis_authorities, - ).unwrap(); + let (new_set_id, new_authorities) = warp_sync_proof.verify(0, genesis_authorities).unwrap(); let expected_authorities = current_authorities .iter() diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 24b60df15febe..76e21215c2457 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -84,14 +84,13 @@ impl InformantDisplay { let diff_bytes_inbound = total_bytes_inbound - self.last_total_bytes_inbound; let diff_bytes_outbound = total_bytes_outbound - self.last_total_bytes_outbound; - let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = - if elapsed > 0 { - self.last_total_bytes_inbound = total_bytes_inbound; - self.last_total_bytes_outbound = total_bytes_outbound; - (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed) - } else { - (diff_bytes_inbound, diff_bytes_outbound) - }; + let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = if elapsed > 0 { + self.last_total_bytes_inbound = total_bytes_inbound; + self.last_total_bytes_outbound = total_bytes_outbound; + (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed) + } else { + (diff_bytes_inbound, diff_bytes_outbound) + }; let (level, status, target) = match ( net_status.sync_state, @@ -102,20 +101,26 @@ impl InformantDisplay { (_, _, _, Some(warp)) => ( "⏩", "Warping".into(), - format!(", {}, ({:.2}) Mib", warp.phase, (warp.total_bytes as f32) / (1024f32 * 1024f32)), + format!( + ", {}, ({:.2}) Mib", + warp.phase, + (warp.total_bytes as f32) / (1024f32 * 1024f32) + ), ), (_, _, Some(state), _) => ( "⚙️ ", "Downloading state".into(), - format!(", {}%, ({:.2}) Mib", state.percentage, (state.size as f32) / (1024f32 * 1024f32)), + format!( + ", {}%, ({:.2}) Mib", + state.percentage, + (state.size as f32) / (1024f32 * 1024f32) + ), ), (SyncState::Idle, _, _, _) => ("💤", "Idle".into(), "".into()), - (SyncState::Downloading, None, _, _) => ("⚙️ ", format!("Preparing{}", speed), "".into()), - (SyncState::Downloading, Some(n), None, _) => ( - "⚙️ ", - format!("Syncing{}", speed), - format!(", target=#{}", n), - ), + (SyncState::Downloading, None, _, _) => + ("⚙️ ", format!("Preparing{}", speed), "".into()), + (SyncState::Downloading, Some(n), None, _) => + ("⚙️ ", format!("Syncing{}", speed), format!(", target=#{}", n)), }; if self.format.enable_color { @@ -157,7 +162,7 @@ impl InformantDisplay { fn speed( best_number: NumberFor, last_number: Option>, - last_update: Instant + last_update: Instant, ) -> String { // Number of milliseconds elapsed since last time. let elapsed_ms = { @@ -170,25 +175,28 @@ fn speed( // Number of blocks that have been imported since last time. let diff = match last_number { None => return String::new(), - Some(n) => best_number.saturating_sub(n) + Some(n) => best_number.saturating_sub(n), }; if let Ok(diff) = TryInto::::try_into(diff) { // If the number of blocks can be converted to a regular integer, then it's easy: just // do the math and turn it into a `f64`. - let speed = diff.saturating_mul(10_000).checked_div(u128::from(elapsed_ms)) - .map_or(0.0, |s| s as f64) / 10.0; + let speed = diff + .saturating_mul(10_000) + .checked_div(u128::from(elapsed_ms)) + .map_or(0.0, |s| s as f64) / + 10.0; format!(" {:4.1} bps", speed) - } else { // If the number of blocks can't be converted to a regular integer, then we need a more // algebraic approach and we stay within the realm of integers. let one_thousand = NumberFor::::from(1_000u32); - let elapsed = NumberFor::::from( - >::try_from(elapsed_ms).unwrap_or(u32::MAX) - ); + let elapsed = + NumberFor::::from(>::try_from(elapsed_ms).unwrap_or(u32::MAX)); - let speed = diff.saturating_mul(one_thousand).checked_div(&elapsed) + let speed = diff + .saturating_mul(one_thousand) + .checked_div(&elapsed) .unwrap_or_else(Zero::zero); format!(" {} bps", speed) } diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index ff6bf09c65688..c7c90a626a34a 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -21,7 +21,7 @@ use ansi_term::Colour; use futures::prelude::*; use futures_timer::Delay; -use log::{info, trace, debug}; +use log::{debug, info, trace}; use parity_util_mem::MallocSizeOf; use sc_client_api::{BlockchainEvents, UsageProvider}; use sc_network::NetworkService; diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 371f0338a5f24..18fb7cb33ef7a 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -17,28 +17,34 @@ // along with this program. If not, see . use crate::{ - config::ProtocolId, bitswap::Bitswap, + config::ProtocolId, discovery::{DiscoveryBehaviour, DiscoveryConfig, DiscoveryOut}, + light_client_requests, peer_info, protocol::{message::Roles, CustomMessageOutcome, NotificationsSink, Protocol}, - peer_info, request_responses, light_client_requests, - ObservedRole, DhtEvent, + request_responses, DhtEvent, ObservedRole, }; use bytes::Bytes; +use codec::Encode; use futures::{channel::oneshot, stream::StreamExt}; -use libp2p::NetworkBehaviour; -use libp2p::core::{Multiaddr, PeerId, PublicKey}; -use libp2p::identify::IdentifyInfo; -use libp2p::kad::record; -use libp2p::swarm::{ - NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters, toggle::Toggle +use libp2p::{ + core::{Multiaddr, PeerId, PublicKey}, + identify::IdentifyInfo, + kad::record, + swarm::{toggle::Toggle, NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters}, + NetworkBehaviour, }; use log::debug; use prost::Message; -use codec::Encode; -use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}}; -use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justifications}; +use sp_consensus::{ + import_queue::{IncomingBlock, Origin}, + BlockOrigin, +}; +use sp_runtime::{ + traits::{Block as BlockT, NumberFor}, + Justifications, +}; use std::{ borrow::Cow, collections::{HashSet, VecDeque}, @@ -48,8 +54,7 @@ use std::{ }; pub use crate::request_responses::{ - ResponseFailure, InboundFailure, RequestFailure, OutboundFailure, RequestId, - IfDisconnected + IfDisconnected, InboundFailure, OutboundFailure, RequestFailure, RequestId, ResponseFailure, }; /// General behaviour of the network. Combines all protocols together. @@ -224,8 +229,9 @@ impl Behaviour { peer_info: peer_info::PeerInfoBehaviour::new(user_agent, local_public_key), discovery: disco_config.finish(), bitswap: bitswap.into(), - request_responses: - request_responses::RequestResponsesBehaviour::new(request_response_protocols.into_iter())?, + request_responses: request_responses::RequestResponsesBehaviour::new( + request_response_protocols.into_iter(), + )?, light_client_request_sender, events: VecDeque::new(), block_request_protocol_name, @@ -248,7 +254,9 @@ impl Behaviour { /// /// Identifies Kademlia instances by their [`ProtocolId`] and kbuckets by the base 2 logarithm /// of their lower bound. - pub fn num_entries_per_kbucket(&mut self) -> impl ExactSizeIterator)> { + pub fn num_entries_per_kbucket( + &mut self, + ) -> impl ExactSizeIterator)> { self.discovery.num_entries_per_kbucket() } @@ -258,7 +266,9 @@ impl Behaviour { } /// Returns the total size in bytes of all the records in the Kademlia record stores. - pub fn kademlia_records_total_size(&mut self) -> impl ExactSizeIterator { + pub fn kademlia_records_total_size( + &mut self, + ) -> impl ExactSizeIterator { self.discovery.kademlia_records_total_size() } @@ -280,7 +290,8 @@ impl Behaviour { pending_response: oneshot::Sender, RequestFailure>>, connect: IfDisconnected, ) { - self.request_responses.send_request(target, protocol, request, pending_response, connect) + self.request_responses + .send_request(target, protocol, request, pending_response, connect) } /// Returns a shared reference to the user protocol. @@ -322,21 +333,20 @@ fn reported_roles_to_observed_role(roles: Roles) -> ObservedRole { } } -impl NetworkBehaviourEventProcess for -Behaviour { +impl NetworkBehaviourEventProcess for Behaviour { fn inject_event(&mut self, event: void::Void) { void::unreachable(event) } } -impl NetworkBehaviourEventProcess> for -Behaviour { +impl NetworkBehaviourEventProcess> for Behaviour { fn inject_event(&mut self, event: CustomMessageOutcome) { match event { CustomMessageOutcome::BlockImport(origin, blocks) => self.events.push_back(BehaviourOut::BlockImport(origin, blocks)), - CustomMessageOutcome::JustificationImport(origin, hash, nb, justification) => - self.events.push_back(BehaviourOut::JustificationImport(origin, hash, nb, justification)), + CustomMessageOutcome::JustificationImport(origin, hash, nb, justification) => self + .events + .push_back(BehaviourOut::JustificationImport(origin, hash, nb, justification)), CustomMessageOutcome::BlockRequest { target, request, pending_response } => { let mut buf = Vec::with_capacity(request.encoded_len()); if let Err(err) = request.encode(&mut buf) { @@ -349,7 +359,11 @@ Behaviour { } self.request_responses.send_request( - &target, &self.block_request_protocol_name, buf, pending_response, IfDisconnected::ImmediateError, + &target, + &self.block_request_protocol_name, + buf, + pending_response, + IfDisconnected::ImmediateError, ); }, CustomMessageOutcome::StateRequest { target, request, pending_response } => { @@ -364,13 +378,21 @@ Behaviour { } self.request_responses.send_request( - &target, &self.state_request_protocol_name, buf, pending_response, IfDisconnected::ImmediateError, + &target, + &self.state_request_protocol_name, + buf, + pending_response, + IfDisconnected::ImmediateError, ); }, - CustomMessageOutcome::WarpSyncRequest { target, request, pending_response } => { + CustomMessageOutcome::WarpSyncRequest { target, request, pending_response } => match &self.warp_sync_protocol_name { Some(name) => self.request_responses.send_request( - &target, name, request.encode(), pending_response, IfDisconnected::ImmediateError, + &target, + name, + request.encode(), + pending_response, + IfDisconnected::ImmediateError, ), None => { log::warn!( @@ -379,11 +401,14 @@ Behaviour { request, ); return - } - } - }, + }, + }, CustomMessageOutcome::NotificationStreamOpened { - remote, protocol, negotiated_fallback, roles, notifications_sink + remote, + protocol, + negotiated_fallback, + roles, + notifications_sink, } => { self.events.push_back(BehaviourOut::NotificationStreamOpened { remote, @@ -393,32 +418,33 @@ Behaviour { notifications_sink: notifications_sink.clone(), }); }, - CustomMessageOutcome::NotificationStreamReplaced { remote, protocol, notifications_sink } => - self.events.push_back(BehaviourOut::NotificationStreamReplaced { - remote, - protocol, - notifications_sink, - }), - CustomMessageOutcome::NotificationStreamClosed { remote, protocol } => - self.events.push_back(BehaviourOut::NotificationStreamClosed { - remote, - protocol, - }), + CustomMessageOutcome::NotificationStreamReplaced { + remote, + protocol, + notifications_sink, + } => self.events.push_back(BehaviourOut::NotificationStreamReplaced { + remote, + protocol, + notifications_sink, + }), + CustomMessageOutcome::NotificationStreamClosed { remote, protocol } => self + .events + .push_back(BehaviourOut::NotificationStreamClosed { remote, protocol }), CustomMessageOutcome::NotificationsReceived { remote, messages } => { self.events.push_back(BehaviourOut::NotificationsReceived { remote, messages }); }, CustomMessageOutcome::PeerNewBest(peer_id, number) => { self.light_client_request_sender.update_best_block(&peer_id, number); - } + }, CustomMessageOutcome::SyncConnected(peer_id) => { self.light_client_request_sender.inject_connected(peer_id); self.events.push_back(BehaviourOut::SyncConnected(peer_id)) - } + }, CustomMessageOutcome::SyncDisconnected(peer_id) => { self.light_client_request_sender.inject_disconnected(peer_id); self.events.push_back(BehaviourOut::SyncDisconnected(peer_id)) - } - CustomMessageOutcome::None => {} + }, + CustomMessageOutcome::None => {}, } } } @@ -427,38 +453,29 @@ impl NetworkBehaviourEventProcess for Behav fn inject_event(&mut self, event: request_responses::Event) { match event { request_responses::Event::InboundRequest { peer, protocol, result } => { - self.events.push_back(BehaviourOut::InboundRequest { + self.events.push_back(BehaviourOut::InboundRequest { peer, protocol, result }); + }, + request_responses::Event::RequestFinished { peer, protocol, duration, result } => { + self.events.push_back(BehaviourOut::RequestFinished { peer, protocol, + duration, result, }); - } - request_responses::Event::RequestFinished { peer, protocol, duration, result } => { - self.events.push_back(BehaviourOut::RequestFinished { - peer, protocol, duration, result, - }); }, - request_responses::Event::ReputationChanges { peer, changes } => { + request_responses::Event::ReputationChanges { peer, changes } => for change in changes { self.substrate.report_peer(peer, change); - } - } + }, } } } -impl NetworkBehaviourEventProcess - for Behaviour { +impl NetworkBehaviourEventProcess for Behaviour { fn inject_event(&mut self, event: peer_info::PeerInfoEvent) { let peer_info::PeerInfoEvent::Identified { peer_id, - info: IdentifyInfo { - protocol_version, - agent_version, - mut listen_addrs, - protocols, - .. - }, + info: IdentifyInfo { protocol_version, agent_version, mut listen_addrs, protocols, .. }, } = event; if listen_addrs.len() > 30 { @@ -477,8 +494,7 @@ impl NetworkBehaviourEventProcess } } -impl NetworkBehaviourEventProcess - for Behaviour { +impl NetworkBehaviourEventProcess for Behaviour { fn inject_event(&mut self, out: DiscoveryOut) { match out { DiscoveryOut::UnroutablePeer(_peer_id) => { @@ -486,27 +502,28 @@ impl NetworkBehaviourEventProcess // to Kademlia is handled by the `Identify` protocol, part of the // `PeerInfoBehaviour`. See the `NetworkBehaviourEventProcess` // implementation for `PeerInfoEvent`. - } + }, DiscoveryOut::Discovered(peer_id) => { self.substrate.add_default_set_discovered_nodes(iter::once(peer_id)); - } + }, DiscoveryOut::ValueFound(results, duration) => { - self.events.push_back(BehaviourOut::Dht(DhtEvent::ValueFound(results), duration)); - } + self.events + .push_back(BehaviourOut::Dht(DhtEvent::ValueFound(results), duration)); + }, DiscoveryOut::ValueNotFound(key, duration) => { self.events.push_back(BehaviourOut::Dht(DhtEvent::ValueNotFound(key), duration)); - } + }, DiscoveryOut::ValuePut(key, duration) => { self.events.push_back(BehaviourOut::Dht(DhtEvent::ValuePut(key), duration)); - } + }, DiscoveryOut::ValuePutFailed(key, duration) => { - self.events.push_back(BehaviourOut::Dht(DhtEvent::ValuePutFailed(key), duration)); - } - DiscoveryOut::RandomKademliaStarted(protocols) => { + self.events + .push_back(BehaviourOut::Dht(DhtEvent::ValuePutFailed(key), duration)); + }, + DiscoveryOut::RandomKademliaStarted(protocols) => for protocol in protocols { self.events.push_back(BehaviourOut::RandomKademliaStarted(protocol)); - } - } + }, } } } @@ -518,22 +535,16 @@ impl Behaviour { _: &mut impl PollParameters, ) -> Poll>> { use light_client_requests::sender::OutEvent; - while let Poll::Ready(Some(event)) = - self.light_client_request_sender.poll_next_unpin(cx) - { + while let Poll::Ready(Some(event)) = self.light_client_request_sender.poll_next_unpin(cx) { match event { - OutEvent::SendRequest { - target, - request, - pending_response, - protocol_name, - } => self.request_responses.send_request( - &target, - &protocol_name, - request, - pending_response, - IfDisconnected::ImmediateError, - ), + OutEvent::SendRequest { target, request, pending_response, protocol_name } => + self.request_responses.send_request( + &target, + &protocol_name, + request, + pending_response, + IfDisconnected::ImmediateError, + ), } } diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 562f029451557..423823d947f04 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -21,15 +21,15 @@ //! The [`Params`] struct is the struct that must be passed in order to initialize the networking. //! See the documentation of [`Params`]. -pub use crate::chain::Client; -pub use crate::on_demand_layer::{AlwaysBadChecker, OnDemand}; -pub use crate::warp_request_handler::WarpSyncProvider; -pub use crate::request_responses::{ - IncomingRequest, - OutgoingResponse, - ProtocolConfig as RequestResponseConfig, +pub use crate::{ + chain::Client, + on_demand_layer::{AlwaysBadChecker, OnDemand}, + request_responses::{ + IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig, + }, + warp_request_handler::WarpSyncProvider, }; -pub use libp2p::{identity, core::PublicKey, wasm_ext::ExtTransport, build_multiaddr}; +pub use libp2p::{build_multiaddr, core::PublicKey, identity, wasm_ext::ExtTransport}; // Note: this re-export shouldn't be part of the public API of the crate and will be removed in // the future. @@ -47,15 +47,19 @@ use libp2p::{ use prometheus_endpoint::Registry; use sp_consensus::{block_validation::BlockAnnounceValidator, import_queue::ImportQueue}; use sp_runtime::traits::Block as BlockT; -use std::{borrow::Cow, convert::TryFrom, future::Future, pin::Pin, str::FromStr}; use std::{ + borrow::Cow, collections::HashMap, + convert::TryFrom, error::Error, fs, + future::Future, io::{self, Write}, net::Ipv4Addr, path::{Path, PathBuf}, + pin::Pin, str, + str::FromStr, sync::Arc, }; use zeroize::Zeroize; @@ -185,7 +189,7 @@ pub enum TransactionImport { } /// Future resolving to transaction import result. -pub type TransactionImportFuture = Pin + Send>>; +pub type TransactionImportFuture = Pin + Send>>; /// Transaction pool interface pub trait TransactionPool: Send + Sync { @@ -196,10 +200,7 @@ pub trait TransactionPool: Send + Sync { /// Import a transaction into the pool. /// /// This will return future. - fn import( - &self, - transaction: B::Extrinsic, - ) -> TransactionImportFuture; + fn import(&self, transaction: B::Extrinsic) -> TransactionImportFuture; /// Notify the pool about transactions broadcast. fn on_broadcasted(&self, propagations: HashMap>); /// Get transaction by hash. @@ -223,16 +224,15 @@ impl TransactionPool for EmptyTransaction Default::default() } - fn import( - &self, - _transaction: B::Extrinsic - ) -> TransactionImportFuture { + fn import(&self, _transaction: B::Extrinsic) -> TransactionImportFuture { Box::pin(future::ready(TransactionImport::KnownGood)) } fn on_broadcasted(&self, _: HashMap>) {} - fn transaction(&self, _h: &H) -> Option { None } + fn transaction(&self, _h: &H) -> Option { + None + } } /// Name of a protocol, transmitted on the wire. Should be unique for each chain. Always UTF-8. @@ -278,10 +278,10 @@ pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { } /// Splits a Multiaddress into a Multiaddress and PeerId. -pub fn parse_addr(mut addr: Multiaddr)-> Result<(PeerId, Multiaddr), ParseErr> { +pub fn parse_addr(mut addr: Multiaddr) -> Result<(PeerId, Multiaddr), ParseErr> { let who = match addr.pop() { - Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key) - .map_err(|_| ParseErr::InvalidPeerId)?, + Some(multiaddr::Protocol::P2p(key)) => + PeerId::from_multihash(key).map_err(|_| ParseErr::InvalidPeerId)?, _ => return Err(ParseErr::PeerIdMissing), }; @@ -329,10 +329,7 @@ impl FromStr for MultiaddrWithPeerId { fn from_str(s: &str) -> Result { let (peer_id, multiaddr) = parse_str_addr(s)?; - Ok(MultiaddrWithPeerId { - peer_id, - multiaddr, - }) + Ok(MultiaddrWithPeerId { peer_id, multiaddr }) } } @@ -510,18 +507,13 @@ impl NetworkConfiguration { /// Create new default configuration for localhost-only connection with random port (useful for testing) pub fn new_local() -> NetworkConfiguration { - let mut config = NetworkConfiguration::new( - "test-node", - "test-client", - Default::default(), - None, - ); - - config.listen_addresses = vec![ - iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect() - ]; + .collect()]; config.allow_non_globals_in_dht = true; config @@ -529,18 +521,13 @@ impl NetworkConfiguration { /// Create new default configuration for localhost-only connection with random port (useful for testing) pub fn new_memory() -> NetworkConfiguration { - let mut config = NetworkConfiguration::new( - "test-node", - "test-client", - Default::default(), - None, - ); - - config.listen_addresses = vec![ - iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) + let mut config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); + + config.listen_addresses = + vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) .chain(iter::once(multiaddr::Protocol::Tcp(0))) - .collect() - ]; + .collect()]; config.allow_non_globals_in_dht = true; config @@ -680,7 +667,7 @@ impl NonReservedPeerMode { #[derive(Clone, Debug)] pub enum NodeKeyConfig { /// A Ed25519 secret key configuration. - Ed25519(Secret) + Ed25519(Secret), } impl Default for NodeKeyConfig { @@ -704,7 +691,7 @@ pub enum Secret { /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. File(PathBuf), /// Always generate a new secret key `K`. - New + New, } impl fmt::Debug for Secret { @@ -731,35 +718,27 @@ impl NodeKeyConfig { pub fn into_keypair(self) -> io::Result { use NodeKeyConfig::*; match self { - Ed25519(Secret::New) => - Ok(Keypair::generate_ed25519()), - - Ed25519(Secret::Input(k)) => - Ok(Keypair::Ed25519(k.into())), - - Ed25519(Secret::File(f)) => - get_secret( - f, - |mut b| { - match String::from_utf8(b.to_vec()) - .ok() - .and_then(|s|{ - if s.len() == 64 { - hex::decode(&s).ok() - } else { - None - }} - ) - { - Some(s) => ed25519::SecretKey::from_bytes(s), - _ => ed25519::SecretKey::from_bytes(&mut b), - } - }, - ed25519::SecretKey::generate, - |b| b.as_ref().to_vec() - ) - .map(ed25519::Keypair::from) - .map(Keypair::Ed25519), + Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), + + Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())), + + Ed25519(Secret::File(f)) => get_secret( + f, + |mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| { + if s.len() == 64 { + hex::decode(&s).ok() + } else { + None + } + }) { + Some(s) => ed25519::SecretKey::from_bytes(s), + _ => ed25519::SecretKey::from_bytes(&mut b), + }, + ed25519::SecretKey::generate, + |b| b.as_ref().to_vec(), + ) + .map(ed25519::Keypair::from) + .map(Keypair::Ed25519), } } } @@ -776,9 +755,9 @@ where W: Fn(&K) -> Vec, { std::fs::read(&file) - .and_then(|mut sk_bytes| - parse(&mut sk_bytes) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))) + .and_then(|mut sk_bytes| { + parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + }) .or_else(|e| { if e.kind() == io::ErrorKind::NotFound { file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?; @@ -796,7 +775,7 @@ where /// Write secret bytes to a file. fn write_secret_file

(path: P, sk_bytes: &[u8]) -> io::Result<()> where - P: AsRef + P: AsRef, { let mut file = open_secret_file(&path)?; file.write_all(sk_bytes) @@ -806,26 +785,19 @@ where #[cfg(unix)] fn open_secret_file

(path: P) -> io::Result where - P: AsRef + P: AsRef, { use std::os::unix::fs::OpenOptionsExt; - fs::OpenOptions::new() - .write(true) - .create_new(true) - .mode(0o600) - .open(path) + fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path) } /// Opens a file containing a secret key in write mode. #[cfg(not(unix))] fn open_secret_file

(path: P) -> Result where - P: AsRef + P: AsRef, { - fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(path) + fs::OpenOptions::new().write(true).create_new(true).open(path) } #[cfg(test)] @@ -841,7 +813,7 @@ mod tests { match kp { Keypair::Ed25519(p) => p.secret().as_ref().iter().cloned().collect(), Keypair::Secp256k1(p) => p.secret().to_bytes().to_vec(), - _ => panic!("Unexpected keypair.") + _ => panic!("Unexpected keypair."), } } diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index d24f9b37c7e95..633baaca47aab 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -247,9 +247,9 @@ mod behaviour; mod chain; -mod peer_info; mod discovery; mod on_demand_layer; +mod peer_info; mod protocol; mod request_responses; mod schema; @@ -257,23 +257,26 @@ mod service; mod transport; mod utils; -pub mod block_request_handler; pub mod bitswap; -pub mod light_client_requests; -pub mod state_request_handler; -pub mod warp_request_handler; +pub mod block_request_handler; pub mod config; pub mod error; +pub mod light_client_requests; pub mod network_state; +pub mod state_request_handler; pub mod transactions; +pub mod warp_request_handler; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; -pub use protocol::{event::{DhtEvent, Event, ObservedRole}, PeerInfo}; -pub use protocol::sync::{SyncState, StateDownloadProgress, WarpSyncProgress, WarpSyncPhase}; +pub use protocol::{ + event::{DhtEvent, Event, ObservedRole}, + sync::{StateDownloadProgress, SyncState, WarpSyncPhase, WarpSyncProgress}, + PeerInfo, +}; pub use service::{ - NetworkService, NetworkWorker, RequestFailure, OutboundFailure, NotificationSender, - NotificationSenderReady, IfDisconnected, + IfDisconnected, NetworkService, NetworkWorker, NotificationSender, NotificationSenderReady, + OutboundFailure, RequestFailure, }; pub use sc_peerset::ReputationChange; diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 3d898d8227f39..4acee1fa8dcd1 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -21,50 +21,65 @@ use crate::{ config::{self, ProtocolId, WarpSyncProvider}, error, request_responses::RequestFailure, - utils::{interval, LruHashSet}, schema::v1::StateResponse, + utils::{interval, LruHashSet}, warp_request_handler::EncodedProof, }; use bytes::Bytes; use codec::{Decode, DecodeAll, Encode}; use futures::{channel::oneshot, prelude::*}; +use libp2p::{ + core::{ + connection::{ConnectionId, ListenerId}, + ConnectedPoint, + }, + request_response::OutboundFailure, + swarm::{ + IntoProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction, PollParameters, + ProtocolsHandler, + }, + Multiaddr, PeerId, +}; +use log::{debug, error, log, trace, warn, Level}; +use message::{ + generic::{Message as GenericMessage, Roles}, + BlockAnnounce, Message, +}; use notifications::{Notifications, NotificationsOut}; -use libp2p::core::{ConnectedPoint, connection::{ConnectionId, ListenerId}}; -use libp2p::request_response::OutboundFailure; -use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; -use libp2p::swarm::{ProtocolsHandler, IntoProtocolsHandler}; -use libp2p::{Multiaddr, PeerId}; -use log::{log, Level, trace, debug, warn, error}; -use message::{BlockAnnounce, Message}; -use message::generic::{Message as GenericMessage, Roles}; -use prometheus_endpoint::{Registry, Gauge, GaugeVec, PrometheusError, Opts, register, U64}; +use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; use prost::Message as _; +use sp_arithmetic::traits::SaturatedConversion; use sp_consensus::{ - BlockOrigin, block_validation::BlockAnnounceValidator, - import_queue::{BlockImportResult, BlockImportError, IncomingBlock, Origin} + import_queue::{BlockImportError, BlockImportResult, IncomingBlock, Origin}, + BlockOrigin, }; use sp_runtime::{ - Justifications, generic::BlockId, - traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero, CheckedSub}, + traits::{Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, Zero}, + Justifications, +}; +use std::{ + borrow::Cow, + collections::{HashMap, HashSet, VecDeque}, + convert::TryFrom as _, + io, iter, + num::NonZeroUsize, + pin::Pin, + sync::Arc, + task::Poll, + time, }; -use sp_arithmetic::traits::SaturatedConversion; use sync::{ChainSync, Status as SyncStatus}; -use std::borrow::Cow; -use std::convert::TryFrom as _; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::sync::Arc; -use std::{io, iter, num::NonZeroUsize, pin::Pin, task::Poll, time}; mod notifications; -pub mod message; pub mod event; +pub mod message; pub mod sync; -pub use notifications::{NotificationsSink, Ready, NotifsHandlerError}; +pub use notifications::{NotificationsSink, NotifsHandlerError, Ready}; /// Interval at which we perform time based maintenance const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100); @@ -135,7 +150,7 @@ impl Metrics { let g = GaugeVec::new( Opts::new( "sync_extra_justifications", - "Number of extra justifications requests" + "Number of extra justifications requests", ), &["status"], )?; @@ -193,10 +208,7 @@ enum PeerRequest { struct Peer { info: PeerInfo, /// Current request, if any. Started by emitting [`CustomMessageOutcome::BlockRequest`]. - request: Option<( - PeerRequest, - oneshot::Receiver, RequestFailure>>, - )>, + request: Option<(PeerRequest, oneshot::Receiver, RequestFailure>>)>, /// Holds a set of blocks known to this peer. known_blocks: LruHashSet, } @@ -230,14 +242,9 @@ impl ProtocolConfig { } else { match self.sync_mode { config::SyncMode::Full => sync::SyncMode::Full, - config::SyncMode::Fast { - skip_proofs, - storage_chain_mode, - } => sync::SyncMode::LightState { - skip_proofs, - storage_chain_mode - }, - config::SyncMode::Warp => sync::SyncMode::Warp + config::SyncMode::Fast { skip_proofs, storage_chain_mode } => + sync::SyncMode::LightState { skip_proofs, storage_chain_mode }, + config::SyncMode::Warp => sync::SyncMode::Warp, } } } @@ -301,7 +308,8 @@ impl Protocol { block_announce_validator, config.max_parallel_downloads, warp_sync_provider, - ).map_err(Box::new)?; + ) + .map_err(Box::new)?; let boot_node_ids = { let mut list = HashSet::new(); @@ -317,7 +325,11 @@ impl Protocol { for reserved in &network_config.default_peers_set.reserved_nodes { imp_p.insert(reserved.peer_id.clone()); } - for reserved in network_config.extra_sets.iter().flat_map(|s| s.set_config.reserved_nodes.iter()) { + for reserved in network_config + .extra_sets + .iter() + .flat_map(|s| s.set_config.reserved_nodes.iter()) + { imp_p.insert(reserved.peer_id.clone()); } imp_p.shrink_to_fit(); @@ -327,7 +339,8 @@ impl Protocol { let mut known_addresses = Vec::new(); let (peerset, peerset_handle) = { - let mut sets = Vec::with_capacity(NUM_HARDCODED_PEERSETS + network_config.extra_sets.len()); + let mut sets = + Vec::with_capacity(NUM_HARDCODED_PEERSETS + network_config.extra_sets.len()); let mut default_sets_reserved = HashSet::new(); for reserved in network_config.default_peers_set.reserved_nodes.iter() { @@ -347,8 +360,8 @@ impl Protocol { out_peers: network_config.default_peers_set.out_peers, bootnodes, reserved_nodes: default_sets_reserved.clone(), - reserved_only: network_config.default_peers_set.non_reserved_mode - == config::NonReservedPeerMode::Deny, + reserved_only: network_config.default_peers_set.non_reserved_mode == + config::NonReservedPeerMode::Deny, }); for set_cfg in &network_config.extra_sets { @@ -370,9 +383,7 @@ impl Protocol { }); } - sc_peerset::Peerset::from_config(sc_peerset::PeersetConfig { - sets, - }) + sc_peerset::Peerset::from_config(sc_peerset::PeersetConfig { sets }) }; let block_announces_protocol: Cow<'static, str> = Cow::from({ @@ -388,12 +399,9 @@ impl Protocol { let best_hash = info.best_hash; let genesis_hash = info.genesis_hash; - let block_announces_handshake = BlockAnnouncesHandshake::::build( - &config, - best_number, - best_hash, - genesis_hash, - ).encode(); + let block_announces_handshake = + BlockAnnouncesHandshake::::build(&config, best_number, best_hash, genesis_hash) + .encode(); let sync_protocol_config = notifications::ProtocolConfig { name: block_announces_protocol, @@ -404,22 +412,22 @@ impl Protocol { Notifications::new( peerset, - iter::once(sync_protocol_config) - .chain(network_config.extra_sets.iter() - .zip(notifications_protocols_handshakes) - .map(|(s, hs)| notifications::ProtocolConfig { + iter::once(sync_protocol_config).chain( + network_config.extra_sets.iter().zip(notifications_protocols_handshakes).map( + |(s, hs)| notifications::ProtocolConfig { name: s.notifications_protocol.clone(), fallback_names: s.fallback_names.clone(), handshake: hs, max_notification_size: s.max_notification_size, - }) + }, ), + ), ) }; let block_announce_data_cache = lru::LruCache::new( - network_config.default_peers_set.in_peers as usize - + network_config.default_peers_set.out_peers as usize, + network_config.default_peers_set.in_peers as usize + + network_config.default_peers_set.out_peers as usize, ); let protocol = Protocol { @@ -433,8 +441,11 @@ impl Protocol { important_peers, peerset_handle: peerset_handle.clone(), behaviour, - notification_protocols: - network_config.extra_sets.iter().map(|s| s.notifications_protocol.clone()).collect(), + notification_protocols: network_config + .extra_sets + .iter() + .map(|s| s.notifications_protocol.clone()) + .collect(), bad_handshake_substreams: Default::default(), metrics: if let Some(r) = metrics_registry { Some(Metrics::register(r)?) @@ -466,8 +477,12 @@ impl Protocol { /// Disconnects the given peer if we are connected to it. pub fn disconnect_peer(&mut self, peer_id: &PeerId, protocol_name: &str) { - if let Some(position) = self.notification_protocols.iter().position(|p| *p == protocol_name) { - self.behaviour.disconnect_peer(peer_id, sc_peerset::SetId::from(position + NUM_HARDCODED_PEERSETS)); + if let Some(position) = self.notification_protocols.iter().position(|p| *p == protocol_name) + { + self.behaviour.disconnect_peer( + peer_id, + sc_peerset::SetId::from(position + NUM_HARDCODED_PEERSETS), + ); } else { log::warn!(target: "sub-libp2p", "disconnect_peer() with invalid protocol name") } @@ -485,10 +500,7 @@ impl Protocol { /// Returns the number of peers we're connected to and that are being queried. pub fn num_active_peers(&self) -> usize { - self.peers - .values() - .filter(|p| p.request.is_some()) - .count() + self.peers.values().filter(|p| p.request.is_some()).count() } /// Current global sync state. @@ -529,12 +541,8 @@ impl Protocol { self.behaviour.set_notif_protocol_handshake( HARDCODED_PEERSETS_SYNC, - BlockAnnouncesHandshake::::build( - &self.config, - number, - hash, - self.genesis_hash, - ).encode() + BlockAnnouncesHandshake::::build(&self.config, number, hash, self.genesis_hash) + .encode(), ); } @@ -571,8 +579,11 @@ impl Protocol { } if let Some(_peer_data) = self.peers.remove(&peer) { - if let Some(sync::OnBlockData::Import(origin, blocks)) = self.sync.peer_disconnected(&peer) { - self.pending_messages.push_back(CustomMessageOutcome::BlockImport(origin, blocks)); + if let Some(sync::OnBlockData::Import(origin, blocks)) = + self.sync.peer_disconnected(&peer) + { + self.pending_messages + .push_back(CustomMessageOutcome::BlockImport(origin, blocks)); } Ok(()) } else { @@ -593,67 +604,76 @@ impl Protocol { request: message::BlockRequest, response: crate::schema::v1::BlockResponse, ) -> CustomMessageOutcome { - let blocks = response.blocks.into_iter().map(|block_data| { - Ok(message::BlockData:: { - hash: Decode::decode(&mut block_data.hash.as_ref())?, - header: if !block_data.header.is_empty() { - Some(Decode::decode(&mut block_data.header.as_ref())?) - } else { - None - }, - body: if request.fields.contains(message::BlockAttributes::BODY) { - Some(block_data.body.iter().map(|body| { - Decode::decode(&mut body.as_ref()) - }).collect::, _>>()?) - } else { - None - }, - indexed_body: if request.fields.contains(message::BlockAttributes::INDEXED_BODY) { - Some(block_data.indexed_body) - } else { - None - }, - receipt: if !block_data.message_queue.is_empty() { - Some(block_data.receipt) - } else { - None - }, - message_queue: if !block_data.message_queue.is_empty() { - Some(block_data.message_queue) - } else { - None - }, - justification: if !block_data.justification.is_empty() { - Some(block_data.justification) - } else if block_data.is_empty_justification { - Some(Vec::new()) - } else { - None - }, - justifications: if !block_data.justifications.is_empty() { - Some(DecodeAll::decode_all(&mut block_data.justifications.as_ref())?) - } else { - None - }, + let blocks = response + .blocks + .into_iter() + .map(|block_data| { + Ok(message::BlockData:: { + hash: Decode::decode(&mut block_data.hash.as_ref())?, + header: if !block_data.header.is_empty() { + Some(Decode::decode(&mut block_data.header.as_ref())?) + } else { + None + }, + body: if request.fields.contains(message::BlockAttributes::BODY) { + Some( + block_data + .body + .iter() + .map(|body| Decode::decode(&mut body.as_ref())) + .collect::, _>>()?, + ) + } else { + None + }, + indexed_body: if request.fields.contains(message::BlockAttributes::INDEXED_BODY) + { + Some(block_data.indexed_body) + } else { + None + }, + receipt: if !block_data.message_queue.is_empty() { + Some(block_data.receipt) + } else { + None + }, + message_queue: if !block_data.message_queue.is_empty() { + Some(block_data.message_queue) + } else { + None + }, + justification: if !block_data.justification.is_empty() { + Some(block_data.justification) + } else if block_data.is_empty_justification { + Some(Vec::new()) + } else { + None + }, + justifications: if !block_data.justifications.is_empty() { + Some(DecodeAll::decode_all(&mut block_data.justifications.as_ref())?) + } else { + None + }, + }) }) - }).collect::, codec::Error>>(); + .collect::, codec::Error>>(); let blocks = match blocks { Ok(blocks) => blocks, Err(err) => { debug!(target: "sync", "Failed to decode block response from {}: {}", peer_id, err); self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); - return CustomMessageOutcome::None; - } + return CustomMessageOutcome::None + }, }; - let block_response = message::BlockResponse:: { - id: request.id, - blocks, - }; + let block_response = message::BlockResponse:: { id: request.id, blocks }; let blocks_range = || match ( - block_response.blocks.first().and_then(|b| b.header.as_ref().map(|h| h.number())), + block_response + .blocks + .first() + .and_then(|b| b.header.as_ref().map(|h| h.number())), block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), ) { (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), @@ -676,20 +696,18 @@ impl Protocol { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); CustomMessageOutcome::None - } + }, } } else { match self.sync.on_block_data(&peer_id, Some(request), block_response) { Ok(sync::OnBlockData::Import(origin, blocks)) => CustomMessageOutcome::BlockImport(origin, blocks), - Ok(sync::OnBlockData::Request(peer, req)) => { - self.prepare_block_request(peer, req) - } + Ok(sync::OnBlockData::Request(peer, req)) => self.prepare_block_request(peer, req), Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); CustomMessageOutcome::None - } + }, } } } @@ -710,7 +728,7 @@ impl Protocol { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); CustomMessageOutcome::None - } + }, } } @@ -730,7 +748,7 @@ impl Protocol { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); CustomMessageOutcome::None - } + }, } } @@ -756,7 +774,7 @@ impl Protocol { if self.peers.contains_key(&who) { log::error!(target: "sync", "Called on_sync_peer_connected with already connected peer {}", who); debug_assert!(false); - return Err(()); + return Err(()) } if status.genesis_hash != self.genesis_hash { @@ -779,7 +797,7 @@ impl Protocol { ); } - return Err(()); + return Err(()) } if self.config.roles.is_light() { @@ -788,14 +806,11 @@ impl Protocol { debug!(target: "sync", "Peer {} is unable to serve light requests", who); self.peerset_handle.report_peer(who.clone(), rep::BAD_ROLE); self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()); + return Err(()) } // we don't interested in peers that are far behind us - let self_best_block = self - .chain - .info() - .best_number; + let self_best_block = self.chain.info().best_number; let blocks_difference = self_best_block .checked_sub(&status.best_number) .unwrap_or_else(Zero::zero) @@ -804,7 +819,7 @@ impl Protocol { debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); self.peerset_handle.report_peer(who.clone(), rep::PEER_BEHIND_US_LIGHT); self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - return Err(()); + return Err(()) } } @@ -812,11 +827,12 @@ impl Protocol { info: PeerInfo { roles: status.roles, best_hash: status.best_hash, - best_number: status.best_number + best_number: status.best_number, }, request: None, - known_blocks: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_BLOCKS) - .expect("Constant is nonzero")), + known_blocks: LruHashSet::new( + NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"), + ), }; let req = if peer.info.roles.is_full() { @@ -826,7 +842,7 @@ impl Protocol { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); return Err(()) - } + }, } } else { None @@ -835,7 +851,8 @@ impl Protocol { debug!(target: "sync", "Connected {}", who); self.peers.insert(who.clone(), peer); - self.pending_messages.push_back(CustomMessageOutcome::PeerNewBest(who.clone(), status.best_number)); + self.pending_messages + .push_back(CustomMessageOutcome::PeerNewBest(who.clone(), status.best_number)); if let Some(req) = req { let event = self.prepare_block_request(who.clone(), req); @@ -854,23 +871,25 @@ impl Protocol { Ok(Some(header)) => header, Ok(None) => { warn!("Trying to announce unknown block: {}", hash); - return; - } + return + }, Err(e) => { warn!("Error reading block header {}: {:?}", hash, e); - return; - } + return + }, }; // don't announce genesis block since it will be ignored if header.number().is_zero() { - return; + return } let is_best = self.chain.info().best_hash == hash; debug!(target: "sync", "Reannouncing block {:?} is_best: {}", hash, is_best); - let data = data.or_else(|| self.block_announce_data_cache.get(&hash).cloned()).unwrap_or_default(); + let data = data + .or_else(|| self.block_announce_data_cache.get(&hash).cloned()) + .unwrap_or_default(); for (who, ref mut peer) in self.peers.iter_mut() { let inserted = peer.known_blocks.insert(hash); @@ -886,11 +905,8 @@ impl Protocol { data: Some(data.clone()), }; - self.behaviour.write_notification( - who, - HARDCODED_PEERSETS_SYNC, - message.encode() - ); + self.behaviour + .write_notification(who, HARDCODED_PEERSETS_SYNC, message.encode()); } } } @@ -908,11 +924,7 @@ impl Protocol { /// in the task before being polled once. So, it is required to call /// [`ChainSync::poll_block_announce_validation`] to ensure that the future is /// registered properly and will wake up the task when being ready. - fn push_block_announce_validation( - &mut self, - who: PeerId, - announce: BlockAnnounce, - ) { + fn push_block_announce_validation(&mut self, who: PeerId, announce: BlockAnnounce) { let hash = announce.header.hash(); let peer = match self.peers.get_mut(&who) { @@ -920,8 +932,8 @@ impl Protocol { None => { log::error!(target: "sync", "Received block announce from disconnected peer {}", who); debug_assert!(false); - return; - } + return + }, }; peer.known_blocks.insert(hash.clone()); @@ -942,8 +954,7 @@ impl Protocol { validation_result: sync::PollBlockAnnounceValidation, ) -> CustomMessageOutcome { let (header, is_best, who) = match validation_result { - sync::PollBlockAnnounceValidation::Skip => - return CustomMessageOutcome::None, + sync::PollBlockAnnounceValidation::Skip => return CustomMessageOutcome::None, sync::PollBlockAnnounceValidation::Nothing { is_best, who, announce } => { self.update_peer_info(&who); @@ -964,7 +975,7 @@ impl Protocol { } else { return CustomMessageOutcome::None } - } + }, sync::PollBlockAnnounceValidation::ImportHeader { announce, is_best, who } => { self.update_peer_info(&who); @@ -975,7 +986,7 @@ impl Protocol { } (announce.header, is_best, who) - } + }, sync::PollBlockAnnounceValidation::Failure { who, disconnect } => { if disconnect { self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); @@ -983,7 +994,7 @@ impl Protocol { self.report_peer(who, rep::BAD_BLOCK_ANNOUNCEMENT); return CustomMessageOutcome::None - } + }, }; let number = *header.number(); @@ -995,39 +1006,32 @@ impl Protocol { None, message::generic::BlockResponse { id: 0, - blocks: vec![ - message::generic::BlockData { - hash: header.hash(), - header: Some(header), - body: None, - indexed_body: None, - receipt: None, - message_queue: None, - justification: None, - justifications: None, - }, - ], + blocks: vec![message::generic::BlockData { + hash: header.hash(), + header: Some(header), + body: None, + indexed_body: None, + receipt: None, + message_queue: None, + justification: None, + justifications: None, + }], }, ); if is_best { - self.pending_messages.push_back( - CustomMessageOutcome::PeerNewBest(who, number), - ); + self.pending_messages.push_back(CustomMessageOutcome::PeerNewBest(who, number)); } match blocks_to_import { - Ok(sync::OnBlockData::Import(origin, blocks)) => { - CustomMessageOutcome::BlockImport(origin, blocks) - }, - Ok(sync::OnBlockData::Request(peer, req)) => { - self.prepare_block_request(peer, req) - }, + Ok(sync::OnBlockData::Import(origin, blocks)) => + CustomMessageOutcome::BlockImport(origin, blocks), + Ok(sync::OnBlockData::Request(peer, req)) => self.prepare_block_request(peer, req), Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu); CustomMessageOutcome::None - } + }, } } @@ -1053,7 +1057,12 @@ impl Protocol { /// Request syncing for the given block from given set of peers. /// Uses `protocol` to queue a new block download request and tries to dispatch all pending /// requests. - pub fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor) { + pub fn set_sync_fork_request( + &mut self, + peers: Vec, + hash: &B::Hash, + number: NumberFor, + ) { self.sync.set_sync_fork_request(peers, hash, number) } @@ -1064,39 +1073,41 @@ impl Protocol { &mut self, imported: usize, count: usize, - results: Vec<(Result>, BlockImportError>, B::Hash)> + results: Vec<(Result>, BlockImportError>, B::Hash)>, ) { - let results = self.sync.on_blocks_processed( - imported, - count, - results, - ); + let results = self.sync.on_blocks_processed(imported, count, results); for result in results { match result { Ok((id, req)) => { - self.pending_messages.push_back( - prepare_block_request(&mut self.peers, id, req) - ); - } + self.pending_messages.push_back(prepare_block_request( + &mut self.peers, + id, + req, + )); + }, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC); self.peerset_handle.report_peer(id, repu) - } + }, } } } /// Call this when a justification has been processed by the import queue, with or without /// errors. - pub fn justification_import_result(&mut self, who: PeerId, hash: B::Hash, number: NumberFor, success: bool) { + pub fn justification_import_result( + &mut self, + who: PeerId, + hash: B::Hash, + number: NumberFor, + success: bool, + ) { self.sync.on_justification_import(hash, number, success); if !success { log::info!("💔 Invalid justification provided by {} for #{}", who, hash); self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC); - self.peerset_handle.report_peer( - who, - sc_peerset::ReputationChange::new_fatal("Invalid justification") - ); + self.peerset_handle + .report_peer(who, sc_peerset::ReputationChange::new_fatal("Invalid justification")); } } @@ -1128,7 +1139,10 @@ impl Protocol { /// Removes a `PeerId` from the list of reserved peers. pub fn remove_set_reserved_peer(&self, protocol: Cow<'static, str>, peer: PeerId) { if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.remove_reserved_peer(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); + self.peerset_handle.remove_reserved_peer( + sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), + peer, + ); } else { log::error!( target: "sub-libp2p", @@ -1141,7 +1155,8 @@ impl Protocol { /// Adds a `PeerId` to the list of reserved peers. pub fn add_set_reserved_peer(&self, protocol: Cow<'static, str>, peer: PeerId) { if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.add_reserved_peer(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); + self.peerset_handle + .add_reserved_peer(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); } else { log::error!( target: "sub-libp2p", @@ -1163,7 +1178,8 @@ impl Protocol { /// Add a peer to a peers set. pub fn add_to_peers_set(&self, protocol: Cow<'static, str>, peer: PeerId) { if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.add_to_peers_set(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); + self.peerset_handle + .add_to_peers_set(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); } else { log::error!( target: "sub-libp2p", @@ -1176,7 +1192,10 @@ impl Protocol { /// Remove a peer from a peers set. pub fn remove_from_peers_set(&self, protocol: Cow<'static, str>, peer: PeerId) { if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) { - self.peerset_handle.remove_from_peers_set(sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), peer); + self.peerset_handle.remove_from_peers_set( + sc_peerset::SetId::from(index + NUM_HARDCODED_PEERSETS), + peer, + ); } else { log::error!( target: "sub-libp2p", @@ -1196,13 +1215,21 @@ impl Protocol { metrics.fork_targets.set(m.fork_targets.into()); metrics.queued_blocks.set(m.queued_blocks.into()); - metrics.justifications.with_label_values(&["pending"]) + metrics + .justifications + .with_label_values(&["pending"]) .set(m.justifications.pending_requests.into()); - metrics.justifications.with_label_values(&["active"]) + metrics + .justifications + .with_label_values(&["active"]) .set(m.justifications.active_requests.into()); - metrics.justifications.with_label_values(&["failed"]) + metrics + .justifications + .with_label_values(&["failed"]) .set(m.justifications.failed_requests.into()); - metrics.justifications.with_label_values(&["importing"]) + metrics + .justifications + .with_label_values(&["importing"]) .set(m.justifications.importing_requests.into()); } } @@ -1233,11 +1260,7 @@ fn prepare_block_request( support_multiple_justifications: true, }; - CustomMessageOutcome::BlockRequest { - target: who, - request: request, - pending_response: tx, - } + CustomMessageOutcome::BlockRequest { target: who, request, pending_response: tx } } fn prepare_state_request( @@ -1250,11 +1273,7 @@ fn prepare_state_request( if let Some(ref mut peer) = peers.get_mut(&who) { peer.request = Some((PeerRequest::State, rx)); } - CustomMessageOutcome::StateRequest { - target: who, - request: request, - pending_response: tx, - } + CustomMessageOutcome::StateRequest { target: who, request, pending_response: tx } } fn prepare_warp_sync_request( @@ -1267,11 +1286,7 @@ fn prepare_warp_sync_request( if let Some(ref mut peer) = peers.get_mut(&who) { peer.request = Some((PeerRequest::WarpProof, rx)); } - CustomMessageOutcome::WarpSyncRequest { - target: who, - request: request, - pending_response: tx, - } + CustomMessageOutcome::WarpSyncRequest { target: who, request, pending_response: tx } } /// Outcome of an incoming custom message. @@ -1287,7 +1302,7 @@ pub enum CustomMessageOutcome { /// See [`crate::Event::NotificationStreamOpened::negotiated_fallback`]. negotiated_fallback: Option>, roles: Roles, - notifications_sink: NotificationsSink + notifications_sink: NotificationsSink, }, /// The [`NotificationsSink`] of some notification protocols need an update. NotificationStreamReplaced { @@ -1296,9 +1311,15 @@ pub enum CustomMessageOutcome { notifications_sink: NotificationsSink, }, /// Notification protocols have been closed with a remote. - NotificationStreamClosed { remote: PeerId, protocol: Cow<'static, str> }, + NotificationStreamClosed { + remote: PeerId, + protocol: Cow<'static, str>, + }, /// Messages have been received on one or more notifications protocols. - NotificationsReceived { remote: PeerId, messages: Vec<(Cow<'static, str>, Bytes)> }, + NotificationsReceived { + remote: PeerId, + messages: Vec<(Cow<'static, str>, Bytes)>, + }, /// A new block request must be emitted. BlockRequest { target: PeerId, @@ -1338,11 +1359,21 @@ impl NetworkBehaviour for Protocol { self.behaviour.addresses_of_peer(peer_id) } - fn inject_connection_established(&mut self, peer_id: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint) { + fn inject_connection_established( + &mut self, + peer_id: &PeerId, + conn: &ConnectionId, + endpoint: &ConnectedPoint, + ) { self.behaviour.inject_connection_established(peer_id, conn, endpoint) } - fn inject_connection_closed(&mut self, peer_id: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint) { + fn inject_connection_closed( + &mut self, + peer_id: &PeerId, + conn: &ConnectionId, + endpoint: &ConnectedPoint, + ) { self.behaviour.inject_connection_closed(peer_id, conn, endpoint) } @@ -1372,9 +1403,9 @@ impl NetworkBehaviour for Protocol { <::Handler as ProtocolsHandler>::InEvent, Self::OutEvent > - > { + >{ if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)); + return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) } // Check for finished outgoing requests. @@ -1388,38 +1419,44 @@ impl NetworkBehaviour for Protocol { let (req, _) = peer.request.take().unwrap(); match req { PeerRequest::Block(req) => { - let protobuf_response = match crate::schema::v1::BlockResponse::decode(&resp[..]) { - Ok(proto) => proto, - Err(e) => { - debug!( - target: "sync", - "Failed to decode block response from peer {:?}: {:?}.", - id, - e - ); - self.peerset_handle.report_peer(id.clone(), rep::BAD_MESSAGE); - self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - continue; - } - }; + let protobuf_response = + match crate::schema::v1::BlockResponse::decode(&resp[..]) { + Ok(proto) => proto, + Err(e) => { + debug!( + target: "sync", + "Failed to decode block response from peer {:?}: {:?}.", + id, + e + ); + self.peerset_handle + .report_peer(id.clone(), rep::BAD_MESSAGE); + self.behaviour + .disconnect_peer(id, HARDCODED_PEERSETS_SYNC); + continue + }, + }; finished_block_requests.push((id.clone(), req, protobuf_response)); }, PeerRequest::State => { - let protobuf_response = match crate::schema::v1::StateResponse::decode(&resp[..]) { - Ok(proto) => proto, - Err(e) => { - debug!( - target: "sync", - "Failed to decode state response from peer {:?}: {:?}.", - id, - e - ); - self.peerset_handle.report_peer(id.clone(), rep::BAD_MESSAGE); - self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - continue; - } - }; + let protobuf_response = + match crate::schema::v1::StateResponse::decode(&resp[..]) { + Ok(proto) => proto, + Err(e) => { + debug!( + target: "sync", + "Failed to decode state response from peer {:?}: {:?}.", + id, + e + ); + self.peerset_handle + .report_peer(id.clone(), rep::BAD_MESSAGE); + self.behaviour + .disconnect_peer(id, HARDCODED_PEERSETS_SYNC); + continue + }, + }; finished_state_requests.push((id.clone(), protobuf_response)); }, @@ -1436,32 +1473,35 @@ impl NetworkBehaviour for Protocol { RequestFailure::Network(OutboundFailure::Timeout) => { self.peerset_handle.report_peer(id.clone(), rep::TIMEOUT); self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - } + }, RequestFailure::Network(OutboundFailure::UnsupportedProtocols) => { self.peerset_handle.report_peer(id.clone(), rep::BAD_PROTOCOL); self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - } + }, RequestFailure::Network(OutboundFailure::DialFailure) => { self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - } + }, RequestFailure::Refused => { self.peerset_handle.report_peer(id.clone(), rep::REFUSED); self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); - } - RequestFailure::Network(OutboundFailure::ConnectionClosed) - | RequestFailure::NotConnected => { + }, + RequestFailure::Network(OutboundFailure::ConnectionClosed) | + RequestFailure::NotConnected => { self.behaviour.disconnect_peer(id, HARDCODED_PEERSETS_SYNC); }, RequestFailure::UnknownProtocol => { - debug_assert!(false, "Block request protocol should always be known."); - } + debug_assert!( + false, + "Block request protocol should always be known." + ); + }, RequestFailure::Obsolete => { debug_assert!( false, "Can not receive `RequestFailure::Obsolete` after dropping the \ response receiver.", ); - } + }, } }, Poll::Ready(Err(oneshot::Canceled)) => { @@ -1520,7 +1560,7 @@ impl NetworkBehaviour for Protocol { } if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)); + return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) } let event = match self.behaviour.poll(cx, params) { @@ -1531,14 +1571,22 @@ impl NetworkBehaviour for Protocol { Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }) => return Poll::Ready(NetworkBehaviourAction::DialPeer { peer_id, condition }), Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }) => - return Poll::Ready(NetworkBehaviourAction::NotifyHandler { peer_id, handler, event }), + return Poll::Ready(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler, + event, + }), Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }) => return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr { address, score }), }; let outcome = match event { NotificationsOut::CustomProtocolOpen { - peer_id, set_id, received_handshake, notifications_sink, negotiated_fallback + peer_id, + set_id, + received_handshake, + notifications_sink, + negotiated_fallback, } => { // Set number 0 is hardcoded the default set of peers we sync from. if set_id == HARDCODED_PEERSETS_SYNC { @@ -1571,16 +1619,21 @@ impl NetworkBehaviour for Protocol { ); self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); CustomMessageOutcome::None - } + }, Err(err) => { - match as DecodeAll>::decode_all(&mut &received_handshake[..]) { + match as DecodeAll>::decode_all( + &mut &received_handshake[..], + ) { Ok(handshake) => { - if self.on_sync_peer_connected(peer_id.clone(), handshake).is_ok() { + if self + .on_sync_peer_connected(peer_id.clone(), handshake) + .is_ok() + { CustomMessageOutcome::SyncConnected(peer_id) } else { CustomMessageOutcome::None } - } + }, Err(err2) => { debug!( target: "sync", @@ -1592,21 +1645,24 @@ impl NetworkBehaviour for Protocol { ); self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); CustomMessageOutcome::None - } + }, } - } + }, } - } else { - match (message::Roles::decode_all(&received_handshake[..]), self.peers.get(&peer_id)) { - (Ok(roles), _) => - CustomMessageOutcome::NotificationStreamOpened { - remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id) - NUM_HARDCODED_PEERSETS].clone(), - negotiated_fallback, - roles, - notifications_sink, - }, + match ( + message::Roles::decode_all(&received_handshake[..]), + self.peers.get(&peer_id), + ) { + (Ok(roles), _) => CustomMessageOutcome::NotificationStreamOpened { + remote: peer_id, + protocol: self.notification_protocols + [usize::from(set_id) - NUM_HARDCODED_PEERSETS] + .clone(), + negotiated_fallback, + roles, + notifications_sink, + }, (Err(_), Some(peer)) if received_handshake.is_empty() => { // As a convenience, we allow opening substreams for "external" // notification protocols with an empty handshake. This fetches the @@ -1614,7 +1670,9 @@ impl NetworkBehaviour for Protocol { // TODO: remove this after https://github.com/paritytech/substrate/issues/5685 CustomMessageOutcome::NotificationStreamOpened { remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id) - NUM_HARDCODED_PEERSETS].clone(), + protocol: self.notification_protocols + [usize::from(set_id) - NUM_HARDCODED_PEERSETS] + .clone(), negotiated_fallback, roles: peer.info.roles, notifications_sink, @@ -1626,11 +1684,11 @@ impl NetworkBehaviour for Protocol { self.behaviour.disconnect_peer(&peer_id, set_id); self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); CustomMessageOutcome::None - } + }, } } - } - NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } => { + }, + NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } => if set_id == HARDCODED_PEERSETS_SYNC { CustomMessageOutcome::None } else if self.bad_handshake_substreams.contains(&(peer_id.clone(), set_id)) { @@ -1638,11 +1696,12 @@ impl NetworkBehaviour for Protocol { } else { CustomMessageOutcome::NotificationStreamReplaced { remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id) - NUM_HARDCODED_PEERSETS].clone(), + protocol: self.notification_protocols + [usize::from(set_id) - NUM_HARDCODED_PEERSETS] + .clone(), notifications_sink, } - } - }, + }, NotificationsOut::CustomProtocolClosed { peer_id, set_id } => { // Set number 0 is hardcoded the default set of peers we sync from. if set_id == HARDCODED_PEERSETS_SYNC { @@ -1664,55 +1723,57 @@ impl NetworkBehaviour for Protocol { } else { CustomMessageOutcome::NotificationStreamClosed { remote: peer_id, - protocol: self.notification_protocols[usize::from(set_id) - NUM_HARDCODED_PEERSETS].clone(), + protocol: self.notification_protocols + [usize::from(set_id) - NUM_HARDCODED_PEERSETS] + .clone(), } } }, - NotificationsOut::Notification { peer_id, set_id, message } => - match set_id { - HARDCODED_PEERSETS_SYNC if self.peers.contains_key(&peer_id) => { - if let Ok(announce) = message::BlockAnnounce::decode(&mut message.as_ref()) { - self.push_block_announce_validation(peer_id, announce); - - // Make sure that the newly added block announce validation future was - // polled once to be registered in the task. - if let Poll::Ready(res) = self.sync.poll_block_announce_validation(cx) { - self.process_block_announce_validation_result(res) - } else { - CustomMessageOutcome::None - } + NotificationsOut::Notification { peer_id, set_id, message } => match set_id { + HARDCODED_PEERSETS_SYNC if self.peers.contains_key(&peer_id) => { + if let Ok(announce) = message::BlockAnnounce::decode(&mut message.as_ref()) { + self.push_block_announce_validation(peer_id, announce); + + // Make sure that the newly added block announce validation future was + // polled once to be registered in the task. + if let Poll::Ready(res) = self.sync.poll_block_announce_validation(cx) { + self.process_block_announce_validation_result(res) } else { - warn!(target: "sub-libp2p", "Failed to decode block announce"); CustomMessageOutcome::None } - } - HARDCODED_PEERSETS_SYNC => { - trace!( - target: "sync", - "Received sync for peer earlier refused by sync layer: {}", - peer_id - ); - CustomMessageOutcome::None - } - _ if self.bad_handshake_substreams.contains(&(peer_id.clone(), set_id)) => { + } else { + warn!(target: "sub-libp2p", "Failed to decode block announce"); CustomMessageOutcome::None } - _ => { - let protocol_name = self.notification_protocols[usize::from(set_id) - NUM_HARDCODED_PEERSETS].clone(); - CustomMessageOutcome::NotificationsReceived { - remote: peer_id, - messages: vec![(protocol_name, message.freeze())], - } + }, + HARDCODED_PEERSETS_SYNC => { + trace!( + target: "sync", + "Received sync for peer earlier refused by sync layer: {}", + peer_id + ); + CustomMessageOutcome::None + }, + _ if self.bad_handshake_substreams.contains(&(peer_id.clone(), set_id)) => + CustomMessageOutcome::None, + _ => { + let protocol_name = self.notification_protocols + [usize::from(set_id) - NUM_HARDCODED_PEERSETS] + .clone(); + CustomMessageOutcome::NotificationsReceived { + remote: peer_id, + messages: vec![(protocol_name, message.freeze())], } - } + }, + }, }; if !matches!(outcome, CustomMessageOutcome::::None) { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome)); + return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome)) } if let Some(message) = self.pending_messages.pop_front() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)); + return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)) } // This block can only be reached if an event was pulled from the behaviour and that @@ -1726,7 +1787,7 @@ impl NetworkBehaviour for Protocol { &mut self, peer_id: Option<&PeerId>, addr: &Multiaddr, - error: &dyn std::error::Error + error: &dyn std::error::Error, ) { self.behaviour.inject_addr_reach_failure(peer_id, addr, error) } diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 54744475c3b6e..c67a5d127d867 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -29,36 +29,41 @@ //! order to update it. //! -use codec::Encode; -use blocks::BlockCollection; -use state::StateSync; -use warp::{WarpSync, WarpSyncProvider, WarpProofRequest}; -use sp_blockchain::{Error as ClientError, HeaderMetadata}; -use sp_consensus::{BlockOrigin, BlockStatus, - block_validation::{BlockAnnounceValidator, Validation}, - import_queue::{IncomingBlock, BlockImportResult, BlockImportError} +use crate::{ + protocol::message::{self, BlockAnnounce, BlockAttributes, BlockRequest, BlockResponse}, + schema::v1::{StateRequest, StateResponse}, }; -use crate::protocol::message::{ - self, BlockAnnounce, BlockAttributes, BlockRequest, BlockResponse, -}; -use crate::schema::v1::{StateResponse, StateRequest}; +use blocks::BlockCollection; +use codec::Encode; use either::Either; use extra_requests::ExtraRequests; +use futures::{stream::FuturesUnordered, task::Poll, Future, FutureExt, StreamExt}; use libp2p::PeerId; -use log::{debug, trace, warn, info, error}; +use log::{debug, error, info, trace, warn}; +use sp_arithmetic::traits::Saturating; +use sp_blockchain::{Error as ClientError, HeaderMetadata}; +use sp_consensus::{ + block_validation::{BlockAnnounceValidator, Validation}, + import_queue::{BlockImportError, BlockImportResult, IncomingBlock}, + BlockOrigin, BlockStatus, +}; use sp_runtime::{ - EncodedJustification, Justifications, generic::BlockId, traits::{ - Block as BlockT, Header as HeaderT, NumberFor, Zero, One, CheckedSub, SaturatedConversion, - Hash, HashFor, + Block as BlockT, CheckedSub, Hash, HashFor, Header as HeaderT, NumberFor, One, + SaturatedConversion, Zero, }, + EncodedJustification, Justifications, }; -use sp_arithmetic::traits::Saturating; +use state::StateSync; use std::{ - fmt, ops::Range, collections::{HashMap, hash_map::Entry, HashSet}, sync::Arc, pin::Pin, + collections::{hash_map::Entry, HashMap, HashSet}, + fmt, + ops::Range, + pin::Pin, + sync::Arc, }; -use futures::{task::Poll, Future, stream::FuturesUnordered, FutureExt, StreamExt}; +use warp::{WarpProofRequest, WarpSync, WarpSyncProvider}; mod blocks; mod extra_requests; @@ -128,7 +133,7 @@ mod rep { pub const BAD_JUSTIFICATION: Rep = Rep::new(-(1 << 16), "Bad justification"); /// Reputation change when a peer sent us invlid ancestry result. - pub const UNKNOWN_ANCESTOR:Rep = Rep::new(-(1 << 16), "DB Error"); + pub const UNKNOWN_ANCESTOR: Rep = Rep::new(-(1 << 16), "DB Error"); /// Peer response data does not have requested bits. pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response"); @@ -144,7 +149,7 @@ impl PendingRequests { match self { PendingRequests::Some(set) => { set.insert(id.clone()); - } + }, PendingRequests::All => {}, } } @@ -209,9 +214,8 @@ pub struct ChainSync { /// Total number of downloaded blocks. downloaded_blocks: usize, /// All block announcement that are currently being validated. - block_announce_validation: FuturesUnordered< - Pin> + Send>> - >, + block_announce_validation: + FuturesUnordered> + Send>>>, /// Stats per peer about the number of concurrent block announce validations. block_announce_validation_per_peer_stats: HashMap, /// State sync in progress, if any. @@ -264,7 +268,7 @@ pub struct PeerInfo { /// Their best block hash. pub best_hash: B::Hash, /// Their best block number. - pub best_number: NumberFor + pub best_number: NumberFor, } struct ForkTarget { @@ -282,11 +286,7 @@ pub enum PeerSyncState { /// Available for sync requests. Available, /// Searching for ancestors the Peer has in common with us. - AncestorSearch { - start: NumberFor, - current: NumberFor, - state: AncestorSearchState, - }, + AncestorSearch { start: NumberFor, current: NumberFor, state: AncestorSearchState }, /// Actively downloading new blocks, starting from the given Number. DownloadingNew(NumberFor), /// Downloading a stale block with given Hash. Stale means that it is a @@ -313,7 +313,7 @@ pub enum SyncState { /// Initial sync is complete, keep-up sync is active. Idle, /// Actively catching up with the chain. - Downloading + Downloading, } /// Reported state download progress. @@ -325,7 +325,6 @@ pub struct StateDownloadProgress { pub size: u64, } - /// Reported warp sync phase. #[derive(Clone, Eq, PartialEq, Debug)] pub enum WarpSyncPhase { @@ -415,7 +414,7 @@ pub enum OnStateData { /// The block and state that should be imported. Import(BlockOrigin, IncomingBlock), /// A new state request needs to be made to the given peer. - Request(PeerId, StateRequest) + Request(PeerId, StateRequest), } /// Result of [`ChainSync::on_warp_sync_data`]. @@ -424,7 +423,7 @@ pub enum OnWarpSyncData { /// Warp proof request is issued. WarpProofRequest(PeerId, warp::WarpProofRequest), /// A new state request needs to be made to the given peer. - StateRequest(PeerId, StateRequest) + StateRequest(PeerId, StateRequest), } /// Result of [`ChainSync::poll_block_announce_validation`]. @@ -488,9 +487,7 @@ enum PreValidateBlockAnnounce { /// An error means that *this* node failed to validate it because some internal error happened. /// If the block announcement was invalid, [`Self::Failure`] is the correct variant to express /// this. - Error { - who: PeerId, - }, + Error { who: PeerId }, /// The block announcement should be skipped. /// /// This should *only* be returned when there wasn't a slot registered @@ -504,15 +501,9 @@ pub enum OnBlockJustification { /// The justification needs no further handling. Nothing, /// The justification should be imported. - Import { - peer: PeerId, - hash: B::Hash, - number: NumberFor, - justifications: Justifications - } + Import { peer: PeerId, hash: B::Hash, number: NumberFor, justifications: Justifications }, } - /// Operation mode. #[derive(Debug, PartialEq, Eq)] pub enum SyncMode { @@ -521,10 +512,7 @@ pub enum SyncMode { // Sync headers and block bodies Full, // Sync headers and the last finalied state - LightState { - storage_chain_mode: bool, - skip_proofs: bool, - }, + LightState { storage_chain_mode: bool, skip_proofs: bool }, // GRANDPA warp sync mode. Warp, } @@ -575,12 +563,15 @@ impl ChainSync { fn required_block_attributes(&self) -> BlockAttributes { match self.mode { - SyncMode::Full => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, + SyncMode::Full => + BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, SyncMode::Light => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION, SyncMode::LightState { storage_chain_mode: false, .. } | SyncMode::Warp => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY, SyncMode::LightState { storage_chain_mode: true, .. } => - BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::INDEXED_BODY, + BlockAttributes::HEADER | + BlockAttributes::JUSTIFICATION | + BlockAttributes::INDEXED_BODY, } } @@ -589,7 +580,7 @@ impl ChainSync { SyncMode::Full => false, SyncMode::Light => true, SyncMode::LightState { .. } => true, - SyncMode::Warp => true, + SyncMode::Warp => true, } } @@ -597,30 +588,30 @@ impl ChainSync { /// /// Returns `None` if the peer is unknown. pub fn peer_info(&self, who: &PeerId) -> Option> { - self.peers.get(who).map(|p| PeerInfo { best_hash: p.best_hash, best_number: p.best_number }) + self.peers + .get(who) + .map(|p| PeerInfo { best_hash: p.best_hash, best_number: p.best_number }) } /// Returns the current sync status. pub fn status(&self) -> Status { let best_seen = self.peers.values().map(|p| p.best_number).max(); - let sync_state = - if let Some(n) = best_seen { - // A chain is classified as downloading if the provided best block is - // more than `MAJOR_SYNC_BLOCKS` behind the best queued block. - if n > self.best_queued_number && n - self.best_queued_number > MAJOR_SYNC_BLOCKS.into() { - SyncState::Downloading - } else { - SyncState::Idle - } + let sync_state = if let Some(n) = best_seen { + // A chain is classified as downloading if the provided best block is + // more than `MAJOR_SYNC_BLOCKS` behind the best queued block. + if n > self.best_queued_number && n - self.best_queued_number > MAJOR_SYNC_BLOCKS.into() + { + SyncState::Downloading } else { SyncState::Idle - }; + } + } else { + SyncState::Idle + }; let warp_sync_progress = match (&self.warp_sync, &self.mode) { - (None, SyncMode::Warp) => Some(WarpSyncProgress { - phase: WarpSyncPhase::AwaitingPeers, - total_bytes: 0 - }), + (None, SyncMode::Warp) => + Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingPeers, total_bytes: 0 }), (Some(sync), _) => Some(sync.progress()), _ => None, }; @@ -638,7 +629,10 @@ impl ChainSync { /// Number of active forks requests. This includes /// requests that are pending or could be issued right away. pub fn num_sync_requests(&self) -> usize { - self.fork_targets.values().filter(|f| f.number <= self.best_queued_number).count() + self.fork_targets + .values() + .filter(|f| f.number <= self.best_queued_number) + .count() } /// Number of downloaded blocks. @@ -649,23 +643,26 @@ impl ChainSync { /// Handle a new connected peer. /// /// Call this method whenever we connect to a new peer. - pub fn new_peer(&mut self, who: PeerId, best_hash: B::Hash, best_number: NumberFor) - -> Result>, BadPeer> - { + pub fn new_peer( + &mut self, + who: PeerId, + best_hash: B::Hash, + best_number: NumberFor, + ) -> Result>, BadPeer> { // There is nothing sync can get from the node that has no blockchain data. match self.block_status(&best_hash) { Err(e) => { debug!(target:"sync", "Error reading blockchain: {:?}", e); Err(BadPeer(who, rep::BLOCKCHAIN_READ_ERROR)) - } + }, Ok(BlockStatus::KnownBad) => { info!("💔 New peer with known bad best block {} ({}).", best_hash, best_number); Err(BadPeer(who, rep::BAD_BLOCK)) - } + }, Ok(BlockStatus::Unknown) => { if best_number.is_zero() { info!("💔 New peer with unknown genesis hash {} ({}).", best_hash, best_number); - return Err(BadPeer(who, rep::GENESIS_MISMATCH)); + return Err(BadPeer(who, rep::GENESIS_MISMATCH)) } // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have // enough to do in the import queue that it's not worth kicking off @@ -677,13 +674,16 @@ impl ChainSync { self.best_queued_hash, self.best_queued_number ); - self.peers.insert(who.clone(), PeerSync { - peer_id: who, - common_number: self.best_queued_number, - best_hash, - best_number, - state: PeerSyncState::Available, - }); + self.peers.insert( + who.clone(), + PeerSync { + peer_id: who, + common_number: self.best_queued_number, + best_hash, + best_number, + state: PeerSyncState::Available, + }, + ); return Ok(None) } @@ -692,7 +692,8 @@ impl ChainSync { if self.warp_sync.is_none() { log::debug!(target: "sync", "Starting warp state sync."); if let Some(provider) = &self.warp_sync_provider { - self.warp_sync = Some(WarpSync::new(self.client.clone(), provider.clone())); + self.warp_sync = + Some(WarpSync::new(self.client.clone(), provider.clone())); } } } @@ -723,38 +724,46 @@ impl ChainSync { start: self.best_queued_number, state: AncestorSearchState::ExponentialBackoff(One::one()), }, - Some(ancestry_request::(common_best)) + Some(ancestry_request::(common_best)), ) }; self.pending_requests.add(&who); - self.peers.insert(who.clone(), PeerSync { - peer_id: who, - common_number: Zero::zero(), - best_hash, - best_number, - state, - }); + self.peers.insert( + who.clone(), + PeerSync { + peer_id: who, + common_number: Zero::zero(), + best_hash, + best_number, + state, + }, + ); Ok(req) - } - Ok(BlockStatus::Queued) | Ok(BlockStatus::InChainWithState) | Ok(BlockStatus::InChainPruned) => { + }, + Ok(BlockStatus::Queued) | + Ok(BlockStatus::InChainWithState) | + Ok(BlockStatus::InChainPruned) => { debug!( target: "sync", "New peer with known best hash {} ({}).", best_hash, best_number, ); - self.peers.insert(who.clone(), PeerSync { - peer_id: who.clone(), - common_number: std::cmp::min(self.best_queued_number, best_number), - best_hash, - best_number, - state: PeerSyncState::Available, - }); + self.peers.insert( + who.clone(), + PeerSync { + peer_id: who.clone(), + common_number: std::cmp::min(self.best_queued_number, best_number), + best_hash, + best_number, + state: PeerSyncState::Available, + }, + ); self.pending_requests.add(&who); Ok(None) - } + }, } } @@ -767,9 +776,8 @@ impl ChainSync { /// Schedule a justification request for the given block. pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { let client = &self.client; - self.extra_justifications.schedule((*hash, number), |base, block| { - is_descendent_of(&**client, base, block) - }) + self.extra_justifications + .schedule((*hash, number), |base, block| is_descendent_of(&**client, base, block)) } /// Clear all pending justification requests. @@ -786,7 +794,9 @@ impl ChainSync { number: NumberFor, ) { if peers.is_empty() { - peers = self.peers.iter() + peers = self + .peers + .iter() // Only request blocks from peers who are ahead or on a par. .filter(|(_, peer)| peer.best_number >= number) .map(|(id, _)| id.clone()) @@ -804,14 +814,14 @@ impl ChainSync { if self.is_known(&hash) { debug!(target: "sync", "Refusing to sync known hash {:?}", hash); - return; + return } trace!(target: "sync", "Downloading requested old fork {:?}", hash); for peer_id in &peers { if let Some(peer) = self.peers.get_mut(peer_id) { - if let PeerSyncState::AncestorSearch {..} = peer.state { - continue; + if let PeerSyncState::AncestorSearch { .. } = peer.state { + continue } if number > peer.best_number { @@ -824,22 +834,24 @@ impl ChainSync { self.fork_targets .entry(hash.clone()) - .or_insert_with(|| ForkTarget { - number, - peers: Default::default(), - parent_hash: None, - }) - .peers.extend(peers); + .or_insert_with(|| ForkTarget { number, peers: Default::default(), parent_hash: None }) + .peers + .extend(peers); } /// Get an iterator over all scheduled justification requests. - pub fn justification_requests(&mut self) -> impl Iterator)> + '_ { + pub fn justification_requests( + &mut self, + ) -> impl Iterator)> + '_ { let peers = &mut self.peers; let mut matcher = self.extra_justifications.matcher(); std::iter::from_fn(move || { if let Some((peer, request)) = matcher.next(&peers) { - peers.get_mut(&peer) - .expect("`Matcher::next` guarantees the `PeerId` comes from the given peers; qed") + peers + .get_mut(&peer) + .expect( + "`Matcher::next` guarantees the `PeerId` comes from the given peers; qed", + ) .state = PeerSyncState::DownloadingJustification(request.0); let req = message::generic::BlockRequest { id: 0, @@ -847,7 +859,7 @@ impl ChainSync { from: message::FromBlock::Hash(request.0), to: None, direction: message::Direction::Ascending, - max: Some(1) + max: Some(1), }; Some((peer, req)) } else { @@ -858,7 +870,8 @@ impl ChainSync { /// Get an iterator over all block requests of all peers. pub fn block_requests(&mut self) -> impl Iterator)> + '_ { - if self.pending_requests.is_empty() || self.state_sync.is_some() || self.warp_sync.is_some() { + if self.pending_requests.is_empty() || self.state_sync.is_some() || self.warp_sync.is_some() + { return Either::Left(std::iter::empty()) } if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { @@ -869,7 +882,8 @@ impl ChainSync { let attrs = self.required_block_attributes(); let blocks = &mut self.blocks; let fork_targets = &mut self.fork_targets; - let last_finalized = std::cmp::min(self.best_queued_number, self.client.info().finalized_number); + let last_finalized = + std::cmp::min(self.best_queued_number, self.client.info().finalized_number); let best_queued = self.best_queued_number; let client = &self.client; let queue = &self.queue_blocks; @@ -885,9 +899,10 @@ impl ChainSync { // number is smaller than the last finalized block number, we should do an ancestor // search to find a better common block. If the queue is full we wait till all blocks are // imported though. - if best_queued.saturating_sub(peer.common_number) > MAX_BLOCKS_TO_LOOK_BACKWARDS.into() - && best_queued < peer.best_number && peer.common_number < last_finalized - && queue.len() <= MAJOR_SYNC_BLOCKS.into() + if best_queued.saturating_sub(peer.common_number) > MAX_BLOCKS_TO_LOOK_BACKWARDS.into() && + best_queued < peer.best_number && + peer.common_number < last_finalized && + queue.len() <= MAJOR_SYNC_BLOCKS.into() { trace!( target: "sync", @@ -922,18 +937,14 @@ impl ChainSync { req, ); Some((id, req)) - } else if let Some((hash, req)) = fork_sync_request( - id, - fork_targets, - best_queued, - last_finalized, - attrs, - |hash| if queue.contains(hash) { - BlockStatus::Queued - } else { - client.block_status(&BlockId::Hash(*hash)).unwrap_or(BlockStatus::Unknown) - }, - ) { + } else if let Some((hash, req)) = + fork_sync_request(id, fork_targets, best_queued, last_finalized, attrs, |hash| { + if queue.contains(hash) { + BlockStatus::Queued + } else { + client.block_status(&BlockId::Hash(*hash)).unwrap_or(BlockStatus::Unknown) + } + }) { trace!(target: "sync", "Downloading fork {:?} from {}", hash, id); peer.state = PeerSyncState::DownloadingStale(hash); Some((id, req)) @@ -948,11 +959,11 @@ impl ChainSync { pub fn state_request(&mut self) -> Option<(PeerId, StateRequest)> { if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { // Only one pending state request is allowed. - return None; + return None } if let Some(sync) = &self.state_sync { if sync.is_complete() { - return None; + return None } for (id, peer) in self.peers.iter_mut() { if peer.state.is_available() && peer.common_number >= sync.target_block_num() { @@ -965,7 +976,7 @@ impl ChainSync { } if let Some(sync) = &self.warp_sync { if sync.is_complete() { - return None; + return None } if let Some(request) = sync.next_state_request() { for (id, peer) in self.peers.iter_mut() { @@ -982,13 +993,17 @@ impl ChainSync { /// Get a warp sync request, if any. pub fn warp_sync_request(&mut self) -> Option<(PeerId, WarpProofRequest)> { - if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpProof) { + if self + .peers + .iter() + .any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpProof) + { // Only one pending state request is allowed. - return None; + return None } if let Some(sync) = &self.warp_sync { if sync.is_complete() { - return None; + return None } if let Some(request) = sync.next_warp_poof_request() { for (id, peer) in self.peers.iter_mut() { @@ -1014,38 +1029,42 @@ impl ChainSync { &mut self, who: &PeerId, request: Option>, - response: BlockResponse + response: BlockResponse, ) -> Result, BadPeer> { self.downloaded_blocks += response.blocks.len(); - let new_blocks: Vec> = - if let Some(peer) = self.peers.get_mut(who) { - let mut blocks = response.blocks; - if request.as_ref().map_or(false, |r| r.direction == message::Direction::Descending) { - trace!(target: "sync", "Reversing incoming block list"); - blocks.reverse() - } - self.pending_requests.add(who); - if let Some(request) = request { - match &mut peer.state { - PeerSyncState::DownloadingNew(start_block) => { - self.blocks.clear_peer_download(who); - let start_block = *start_block; - peer.state = PeerSyncState::Available; - validate_blocks::(&blocks, who, Some(request))?; - self.blocks.insert(start_block, blocks, who.clone()); - self.drain_blocks() + let new_blocks: Vec> = if let Some(peer) = self.peers.get_mut(who) { + let mut blocks = response.blocks; + if request + .as_ref() + .map_or(false, |r| r.direction == message::Direction::Descending) + { + trace!(target: "sync", "Reversing incoming block list"); + blocks.reverse() + } + self.pending_requests.add(who); + if let Some(request) = request { + match &mut peer.state { + PeerSyncState::DownloadingNew(start_block) => { + self.blocks.clear_peer_download(who); + let start_block = *start_block; + peer.state = PeerSyncState::Available; + validate_blocks::(&blocks, who, Some(request))?; + self.blocks.insert(start_block, blocks, who.clone()); + self.drain_blocks() + }, + PeerSyncState::DownloadingStale(_) => { + peer.state = PeerSyncState::Available; + if blocks.is_empty() { + debug!(target: "sync", "Empty block response from {}", who); + return Err(BadPeer(who.clone(), rep::NO_BLOCK)) } - PeerSyncState::DownloadingStale(_) => { - peer.state = PeerSyncState::Available; - if blocks.is_empty() { - debug!(target: "sync", "Empty block response from {}", who); - return Err(BadPeer(who.clone(), rep::NO_BLOCK)); - } - validate_blocks::(&blocks, who, Some(request))?; - blocks.into_iter().map(|b| { - let justifications = b.justifications.or( - legacy_justification_mapping(b.justification) - ); + validate_blocks::(&blocks, who, Some(request))?; + blocks + .into_iter() + .map(|b| { + let justifications = b + .justifications + .or(legacy_justification_mapping(b.justification)); IncomingBlock { hash: b.hash, header: b.header, @@ -1058,111 +1077,115 @@ impl ChainSync { skip_execution: self.skip_execution(), state: None, } - }).collect() + }) + .collect() + }, + PeerSyncState::AncestorSearch { current, start, state } => { + let matching_hash = match (blocks.get(0), self.client.hash(*current)) { + (Some(block), Ok(maybe_our_block_hash)) => { + trace!( + target: "sync", + "Got ancestry block #{} ({}) from peer {}", + current, + block.hash, + who, + ); + maybe_our_block_hash.filter(|x| x == &block.hash) + }, + (None, _) => { + debug!( + target: "sync", + "Invalid response when searching for ancestor from {}", + who, + ); + return Err(BadPeer(who.clone(), rep::UNKNOWN_ANCESTOR)) + }, + (_, Err(e)) => { + info!( + target: "sync", + "❌ Error answering legitimate blockchain query: {:?}", + e, + ); + return Err(BadPeer(who.clone(), rep::BLOCKCHAIN_READ_ERROR)) + }, + }; + if matching_hash.is_some() { + if *start < self.best_queued_number && + self.best_queued_number <= peer.best_number + { + // We've made progress on this chain since the search was started. + // Opportunistically set common number to updated number + // instead of the one that started the search. + peer.common_number = self.best_queued_number; + } else if peer.common_number < *current { + peer.common_number = *current; + } } - PeerSyncState::AncestorSearch { current, start, state } => { - let matching_hash = match (blocks.get(0), self.client.hash(*current)) { - (Some(block), Ok(maybe_our_block_hash)) => { - trace!( - target: "sync", - "Got ancestry block #{} ({}) from peer {}", - current, - block.hash, - who, - ); - maybe_our_block_hash.filter(|x| x == &block.hash) - }, - (None, _) => { - debug!( - target: "sync", - "Invalid response when searching for ancestor from {}", - who, - ); - return Err(BadPeer(who.clone(), rep::UNKNOWN_ANCESTOR)) - }, - (_, Err(e)) => { - info!( - target: "sync", - "❌ Error answering legitimate blockchain query: {:?}", - e, - ); - return Err(BadPeer(who.clone(), rep::BLOCKCHAIN_READ_ERROR)) - } + if matching_hash.is_none() && current.is_zero() { + trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); + return Err(BadPeer(who.clone(), rep::GENESIS_MISMATCH)) + } + if let Some((next_state, next_num)) = + handle_ancestor_search_state(state, *current, matching_hash.is_some()) + { + peer.state = PeerSyncState::AncestorSearch { + current: next_num, + start: *start, + state: next_state, }; - if matching_hash.is_some() { - if *start < self.best_queued_number && self.best_queued_number <= peer.best_number { - // We've made progress on this chain since the search was started. - // Opportunistically set common number to updated number - // instead of the one that started the search. - peer.common_number = self.best_queued_number; - } - else if peer.common_number < *current { - peer.common_number = *current; - } - } - if matching_hash.is_none() && current.is_zero() { - trace!(target:"sync", "Ancestry search: genesis mismatch for peer {}", who); - return Err(BadPeer(who.clone(), rep::GENESIS_MISMATCH)) - } - if let Some((next_state, next_num)) = - handle_ancestor_search_state(state, *current, matching_hash.is_some()) + return Ok(OnBlockData::Request( + who.clone(), + ancestry_request::(next_num), + )) + } else { + // Ancestry search is complete. Check if peer is on a stale fork unknown to us and + // add it to sync targets if necessary. + trace!( + target: "sync", + "Ancestry search complete. Ours={} ({}), Theirs={} ({}), Common={:?} ({})", + self.best_queued_hash, + self.best_queued_number, + peer.best_hash, + peer.best_number, + matching_hash, + peer.common_number, + ); + if peer.common_number < peer.best_number && + peer.best_number < self.best_queued_number { - peer.state = PeerSyncState::AncestorSearch { - current: next_num, - start: *start, - state: next_state, - }; - return Ok( - OnBlockData::Request(who.clone(), ancestry_request::(next_num)) - ) - } else { - // Ancestry search is complete. Check if peer is on a stale fork unknown to us and - // add it to sync targets if necessary. trace!( target: "sync", - "Ancestry search complete. Ours={} ({}), Theirs={} ({}), Common={:?} ({})", - self.best_queued_hash, - self.best_queued_number, + "Added fork target {} for {}", peer.best_hash, - peer.best_number, - matching_hash, - peer.common_number, + who, ); - if peer.common_number < peer.best_number - && peer.best_number < self.best_queued_number - { - trace!( - target: "sync", - "Added fork target {} for {}", - peer.best_hash, - who, - ); - self.fork_targets - .entry(peer.best_hash.clone()) - .or_insert_with(|| ForkTarget { - number: peer.best_number, - parent_hash: None, - peers: Default::default(), - }) - .peers.insert(who.clone()); - } - peer.state = PeerSyncState::Available; - Vec::new() + self.fork_targets + .entry(peer.best_hash.clone()) + .or_insert_with(|| ForkTarget { + number: peer.best_number, + parent_hash: None, + peers: Default::default(), + }) + .peers + .insert(who.clone()); } - }, - PeerSyncState::Available - | PeerSyncState::DownloadingJustification(..) - | PeerSyncState::DownloadingState - | PeerSyncState::DownloadingWarpProof - => Vec::new() - } - } else { - // When request.is_none() this is a block announcement. Just accept blocks. - validate_blocks::(&blocks, who, None)?; - blocks.into_iter().map(|b| { - let justifications = b.justifications.or( - legacy_justification_mapping(b.justification) - ); + peer.state = PeerSyncState::Available; + Vec::new() + } + }, + PeerSyncState::Available | + PeerSyncState::DownloadingJustification(..) | + PeerSyncState::DownloadingState | + PeerSyncState::DownloadingWarpProof => Vec::new(), + } + } else { + // When request.is_none() this is a block announcement. Just accept blocks. + validate_blocks::(&blocks, who, None)?; + blocks + .into_iter() + .map(|b| { + let justifications = + b.justifications.or(legacy_justification_mapping(b.justification)); IncomingBlock { hash: b.hash, header: b.header, @@ -1175,12 +1198,13 @@ impl ChainSync { skip_execution: true, state: None, } - }).collect() - } - } else { - // We don't know of this peer, so we also did not request anything from it. - return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)); - }; + }) + .collect() + } + } else { + // We don't know of this peer, so we also did not request anything from it. + return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)) + }; Ok(self.validate_and_queue_blocks(new_blocks)) } @@ -1213,7 +1237,7 @@ impl ChainSync { sync.import_state(response) } else { debug!(target: "sync", "Ignored obsolete state response from {}", who); - return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)); + return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)) }; match import_result { @@ -1233,14 +1257,13 @@ impl ChainSync { }; debug!(target: "sync", "State sync is complete. Import is queued"); Ok(OnStateData::Import(origin, block)) - } - state::ImportResult::Continue(request) => { - Ok(OnStateData::Request(who.clone(), request)) - } + }, + state::ImportResult::Continue(request) => + Ok(OnStateData::Request(who.clone(), request)), state::ImportResult::BadResponse => { debug!(target: "sync", "Bad state data received from {}", who); Err(BadPeer(who.clone(), rep::BAD_BLOCK)) - } + }, } } @@ -1262,20 +1285,18 @@ impl ChainSync { sync.import_warp_proof(response) } else { debug!(target: "sync", "Ignored obsolete warp sync response from {}", who); - return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)); + return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)) }; match import_result { - warp::WarpProofImportResult::StateRequest(request) => { - Ok(OnWarpSyncData::StateRequest(who.clone(), request)) - } - warp::WarpProofImportResult::WarpProofRequest(request) => { - Ok(OnWarpSyncData::WarpProofRequest(who.clone(), request)) - } + warp::WarpProofImportResult::StateRequest(request) => + Ok(OnWarpSyncData::StateRequest(who.clone(), request)), + warp::WarpProofImportResult::WarpProofRequest(request) => + Ok(OnWarpSyncData::WarpProofRequest(who.clone(), request)), warp::WarpProofImportResult::BadResponse => { debug!(target: "sync", "Bad proof data received from {}", who); Err(BadPeer(who.clone(), rep::BAD_BLOCK)) - } + }, } } @@ -1295,7 +1316,10 @@ impl ChainSync { BlockOrigin::NetworkInitialSync }; - if let Some((h, n)) = new_blocks.last().and_then(|b| b.header.as_ref().map(|h| (&b.hash, *h.number()))) { + if let Some((h, n)) = new_blocks + .last() + .and_then(|b| b.header.as_ref().map(|h| (&b.hash, *h.number()))) + { trace!( target:"sync", "Accepted {} blocks ({:?}) with origin {:?}", @@ -1315,16 +1339,17 @@ impl ChainSync { /// /// Returns `Some` if this produces a justification that must be imported /// into the import queue. - pub fn on_block_justification - (&mut self, who: PeerId, response: BlockResponse) -> Result, BadPeer> - { - let peer = - if let Some(peer) = self.peers.get_mut(&who) { - peer - } else { - error!(target: "sync", "💔 Called on_block_justification with a bad peer ID"); - return Ok(OnBlockJustification::Nothing) - }; + pub fn on_block_justification( + &mut self, + who: PeerId, + response: BlockResponse, + ) -> Result, BadPeer> { + let peer = if let Some(peer) = self.peers.get_mut(&who) { + peer + } else { + error!(target: "sync", "💔 Called on_block_justification with a bad peer ID"); + return Ok(OnBlockJustification::Nothing) + }; self.pending_requests.add(&who); if let PeerSyncState::DownloadingJustification(hash) = peer.state { @@ -1337,7 +1362,7 @@ impl ChainSync { target: "sync", "💔 Invalid block justification provided by {}: requested: {:?} got: {:?}", who, hash, block.hash ); - return Err(BadPeer(who, rep::BAD_JUSTIFICATION)); + return Err(BadPeer(who, rep::BAD_JUSTIFICATION)) } block.justifications.or(legacy_justification_mapping(block.justification)) @@ -1353,9 +1378,8 @@ impl ChainSync { None }; - if let Some((peer, hash, number, j)) = self - .extra_justifications - .on_response(who, justification) + if let Some((peer, hash, number, j)) = + self.extra_justifications.on_response(who, justification) { return Ok(OnBlockJustification::Import { peer, hash, number, justifications: j }) } @@ -1386,7 +1410,7 @@ impl ChainSync { } for (result, hash) in results { if has_error { - continue; + continue } if result.is_err() { @@ -1398,7 +1422,7 @@ impl ChainSync { if let Some(peer) = who.and_then(|p| self.peers.get_mut(&p)) { peer.update_common_number(number); } - } + }, Ok(BlockImportResult::ImportedUnknown(number, aux, who)) => { if aux.clear_justification_requests { trace!( @@ -1430,7 +1454,8 @@ impl ChainSync { if let Some(peer) = who.and_then(|p| self.peers.get_mut(&p)) { peer.update_common_number(number); } - let state_sync_complete = self.state_sync.as_ref().map_or(false, |s| s.target() == hash); + let state_sync_complete = + self.state_sync.as_ref().map_or(false, |s| s.target() == hash); if state_sync_complete { info!( target: "sync", @@ -1441,7 +1466,8 @@ impl ChainSync { self.mode = SyncMode::Full; output.extend(self.restart()); } - let warp_sync_complete = self.warp_sync.as_ref().map_or(false, |s| s.target_block_hash() == hash); + let warp_sync_complete = + self.warp_sync.as_ref().map_or(false, |s| s.target_block_hash() == hash); if warp_sync_complete { info!( target: "sync", @@ -1453,7 +1479,7 @@ impl ChainSync { output.extend(self.restart()); } }, - Err(BlockImportError::IncompleteHeader(who)) => { + Err(BlockImportError::IncompleteHeader(who)) => if let Some(peer) = who { warn!( target: "sync", @@ -1461,9 +1487,8 @@ impl ChainSync { ); output.push(Err(BadPeer(peer, rep::INCOMPLETE_HEADER))); output.extend(self.restart()); - } - }, - Err(BlockImportError::VerificationFailed(who, e)) => { + }, + Err(BlockImportError::VerificationFailed(who, e)) => if let Some(peer) = who { warn!( target: "sync", @@ -1474,9 +1499,8 @@ impl ChainSync { ); output.push(Err(BadPeer(peer, rep::VERIFICATION_FAIL))); output.extend(self.restart()); - } - }, - Err(BlockImportError::BadBlock(who)) => { + }, + Err(BlockImportError::BadBlock(who)) => if let Some(peer) = who { warn!( target: "sync", @@ -1485,22 +1509,20 @@ impl ChainSync { peer, ); output.push(Err(BadPeer(peer, rep::BAD_BLOCK))); - } - }, + }, Err(BlockImportError::MissingState) => { // This may happen if the chain we were requesting upon has been discarded // in the meantime because other chain has been finalized. // Don't mark it as bad as it still may be synced if explicitly requested. trace!(target: "sync", "Obsolete block {:?}", hash); }, - e @ Err(BlockImportError::UnknownParent) | - e @ Err(BlockImportError::Other(_)) => { + e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => { warn!(target: "sync", "💔 Error importing block {:?}: {:?}", hash, e); self.state_sync = None; self.warp_sync = None; output.extend(self.restart()); }, - Err(BlockImportError::Cancelled) => {} + Err(BlockImportError::Cancelled) => {}, }; } @@ -1512,7 +1534,8 @@ impl ChainSync { /// with or without errors. pub fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { let finalization_result = if success { Ok((hash, number)) } else { Err(()) }; - self.extra_justifications.try_finalize_root((hash, number), finalization_result, true); + self.extra_justifications + .try_finalize_root((hash, number), finalization_result, true); self.pending_requests.set_all(); } @@ -1524,12 +1547,10 @@ impl ChainSync { }); if let SyncMode::LightState { skip_proofs, .. } = &self.mode { - if self.state_sync.is_none() - && !self.peers.is_empty() - && self.queue_blocks.is_empty() - { + if self.state_sync.is_none() && !self.peers.is_empty() && self.queue_blocks.is_empty() { // Finalized a recent block. - let mut heads: Vec<_> = self.peers.iter().map(|(_, peer)| peer.best_number).collect(); + let mut heads: Vec<_> = + self.peers.iter().map(|(_, peer)| peer.best_number).collect(); heads.sort(); let median = heads[heads.len() / 2]; if number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median { @@ -1540,7 +1561,8 @@ impl ChainSync { number, hash, ); - self.state_sync = Some(StateSync::new(self.client.clone(), header, *skip_proofs)); + self.state_sync = + Some(StateSync::new(self.client.clone(), header, *skip_proofs)); } } } @@ -1568,15 +1590,12 @@ impl ChainSync { self.best_queued_hash = *hash; // Update common blocks for (n, peer) in self.peers.iter_mut() { - if let PeerSyncState::AncestorSearch {..} = peer.state { + if let PeerSyncState::AncestorSearch { .. } = peer.state { // Wait for ancestry search to complete first. - continue; + continue } - let new_common_number = if peer.best_number >= number { - number - } else { - peer.best_number - }; + let new_common_number = + if peer.best_number >= number { number } else { peer.best_number }; trace!( target: "sync", "Updating peer {} info, ours={}, common={}->{}, their best={}", @@ -1603,7 +1622,10 @@ impl ChainSync { /// /// It is *required* to call [`Self::peer_block_announce_validation_finished`] when the /// validation is finished to clear the slot. - fn has_slot_for_block_announce_validation(&mut self, peer: &PeerId) -> HasSlotForBlockAnnounceValidation { + fn has_slot_for_block_announce_validation( + &mut self, + peer: &PeerId, + ) -> HasSlotForBlockAnnounceValidation { if self.block_announce_validation.len() >= MAX_CONCURRENT_BLOCK_ANNOUNCE_VALIDATIONS { return HasSlotForBlockAnnounceValidation::TotalMaximumSlotsReached } @@ -1646,15 +1668,18 @@ impl ChainSync { ); if number.is_zero() { - self.block_announce_validation.push(async move { - warn!( - target: "sync", - "💔 Ignored genesis block (#0) announcement from {}: {}", - who, - hash, - ); - PreValidateBlockAnnounce::Skip - }.boxed()); + self.block_announce_validation.push( + async move { + warn!( + target: "sync", + "💔 Ignored genesis block (#0) announcement from {}: {}", + who, + hash, + ); + PreValidateBlockAnnounce::Skip + } + .boxed(), + ); return } @@ -1662,18 +1687,21 @@ impl ChainSync { match self.has_slot_for_block_announce_validation(&who) { HasSlotForBlockAnnounceValidation::Yes => {}, HasSlotForBlockAnnounceValidation::TotalMaximumSlotsReached => { - self.block_announce_validation.push(async move { - warn!( - target: "sync", - "💔 Ignored block (#{} -- {}) announcement from {} because all validation slots are occupied.", - number, - hash, - who, - ); - PreValidateBlockAnnounce::Skip - }.boxed()); + self.block_announce_validation.push( + async move { + warn!( + target: "sync", + "💔 Ignored block (#{} -- {}) announcement from {} because all validation slots are occupied.", + number, + hash, + who, + ); + PreValidateBlockAnnounce::Skip + } + .boxed(), + ); return - } + }, HasSlotForBlockAnnounceValidation::MaximumPeerSlotsReached => { self.block_announce_validation.push(async move { warn!( @@ -1686,7 +1714,7 @@ impl ChainSync { PreValidateBlockAnnounce::Skip }.boxed()); return - } + }, } // Let external validator check the block announcement. @@ -1694,33 +1722,36 @@ impl ChainSync { let future = self.block_announce_validator.validate(&header, assoc_data); let hash = hash.clone(); - self.block_announce_validation.push(async move { - match future.await { - Ok(Validation::Success { is_new_best }) => PreValidateBlockAnnounce::Process { - is_new_best: is_new_best || is_best, - announce, - who, - }, - Ok(Validation::Failure { disconnect }) => { - debug!( - target: "sync", - "Block announcement validation of block {:?} from {} failed", - hash, + self.block_announce_validation.push( + async move { + match future.await { + Ok(Validation::Success { is_new_best }) => PreValidateBlockAnnounce::Process { + is_new_best: is_new_best || is_best, + announce, who, - ); - PreValidateBlockAnnounce::Failure { who, disconnect } - } - Err(e) => { - debug!( - target: "sync", - "💔 Block announcement validation of block {:?} errored: {}", - hash, - e, - ); - PreValidateBlockAnnounce::Error { who } + }, + Ok(Validation::Failure { disconnect }) => { + debug!( + target: "sync", + "Block announcement validation of block {:?} from {} failed", + hash, + who, + ); + PreValidateBlockAnnounce::Failure { who, disconnect } + }, + Err(e) => { + debug!( + target: "sync", + "💔 Block announcement validation of block {:?} errored: {}", + hash, + e, + ); + PreValidateBlockAnnounce::Error { who } + }, } } - }.boxed()); + .boxed(), + ); } /// Poll block announce validation. @@ -1771,7 +1802,7 @@ impl ChainSync { if *entry.get() == 0 { entry.remove(); } - } + }, } } @@ -1790,9 +1821,8 @@ impl ChainSync { ); return PollBlockAnnounceValidation::Failure { who, disconnect } }, - PreValidateBlockAnnounce::Process { announce, is_new_best, who } => { - (announce, is_new_best, who) - }, + PreValidateBlockAnnounce::Process { announce, is_new_best, who } => + (announce, is_new_best, who), PreValidateBlockAnnounce::Error { .. } | PreValidateBlockAnnounce::Skip => { debug!( target: "sync", @@ -1812,7 +1842,8 @@ impl ChainSync { let number = *announce.header.number(); let hash = announce.header.hash(); - let parent_status = self.block_status(announce.header.parent_hash()).unwrap_or(BlockStatus::Unknown); + let parent_status = + self.block_status(announce.header.parent_hash()).unwrap_or(BlockStatus::Unknown); let known_parent = parent_status != BlockStatus::Unknown; let ancient_parent = parent_status == BlockStatus::InChainPruned; @@ -1830,7 +1861,7 @@ impl ChainSync { peer.best_hash = hash; } - if let PeerSyncState::AncestorSearch {..} = peer.state { + if let PeerSyncState::AncestorSearch { .. } = peer.state { trace!(target: "sync", "Peer state is ancestor search."); return PollBlockAnnounceValidation::Nothing { is_best, who, announce } } @@ -1840,8 +1871,8 @@ impl ChainSync { if is_best { if known && self.best_queued_number >= number { peer.update_common_number(number); - } else if announce.header.parent_hash() == &self.best_queued_hash - || known_parent && self.best_queued_number >= number + } else if announce.header.parent_hash() == &self.best_queued_hash || + known_parent && self.best_queued_number >= number { peer.update_common_number(number - One::one()); } @@ -1895,7 +1926,8 @@ impl ChainSync { parent_hash: Some(*announce.header.parent_hash()), peers: Default::default(), }) - .peers.insert(who.clone()); + .peers + .insert(who.clone()); } PollBlockAnnounceValidation::Nothing { is_best, who, announce } @@ -1943,9 +1975,9 @@ impl ChainSync { // We make sure our commmon number is at least something we have. p.common_number = self.best_queued_number; self.peers.insert(id, p); - return None; - } - _ => {} + return None + }, + _ => {}, } // handle peers that were in other states. @@ -1960,7 +1992,7 @@ impl ChainSync { /// Find a block to start sync from. If we sync with state, that's the latest block we have state for. fn reset_sync_start_point(&mut self) -> Result<(), ClientError> { let info = self.client.info(); - if matches!(self.mode, SyncMode::LightState {..}) && info.finalized_state.is_some() { + if matches!(self.mode, SyncMode::LightState { .. }) && info.finalized_state.is_some() { log::warn!( target: "sync", "Can't use fast sync mode with a partially synced database. Reverting to full sync mode." @@ -1978,7 +2010,9 @@ impl ChainSync { self.best_queued_hash = info.best_hash; self.best_queued_number = info.best_number; if self.mode == SyncMode::Full { - if self.client.block_status(&BlockId::hash(info.best_hash))? != BlockStatus::InChainWithState { + if self.client.block_status(&BlockId::hash(info.best_hash))? != + BlockStatus::InChainWithState + { self.import_existing = true; // Latest state is missing, start with the last finalized state or genesis instead. if let Some((hash, number)) = info.finalized_state { @@ -2011,7 +2045,9 @@ impl ChainSync { /// Is any peer downloading the given hash? fn is_already_downloading(&self, hash: &B::Hash) -> bool { - self.peers.iter().any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) + self.peers + .iter() + .any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash)) } /// Return some key metrics. @@ -2021,7 +2057,7 @@ impl ChainSync { queued_blocks: self.queue_blocks.len().try_into().unwrap_or(std::u32::MAX), fork_targets: self.fork_targets.len().try_into().unwrap_or(std::u32::MAX), justifications: self.extra_justifications.metrics(), - _priv: () + _priv: (), } } @@ -2031,9 +2067,10 @@ impl ChainSync { .drain(self.best_queued_number + One::one()) .into_iter() .map(|block_data| { - let justifications = block_data.block.justifications.or( - legacy_justification_mapping(block_data.block.justification) - ); + let justifications = block_data + .block + .justifications + .or(legacy_justification_mapping(block_data.block.justification)); IncomingBlock { hash: block_data.block.hash, header: block_data.block.header, @@ -2046,16 +2083,18 @@ impl ChainSync { skip_execution: self.skip_execution(), state: None, } - }).collect() + }) + .collect() } - } // This is purely during a backwards compatible transitionary period and should be removed // once we can assume all nodes can send and receive multiple Justifications // The ID tag is hardcoded here to avoid depending on the GRANDPA crate. // See: https://github.com/paritytech/substrate/issues/8172 -fn legacy_justification_mapping(justification: Option) -> Option { +fn legacy_justification_mapping( + justification: Option, +) -> Option { justification.map(|just| (*b"FRNK", just).into()) } @@ -2064,7 +2103,7 @@ pub(crate) struct Metrics { pub(crate) queued_blocks: u32, pub(crate) fork_targets: u32, pub(crate) justifications: extra_requests::Metrics, - _priv: () + _priv: (), } /// Request the ancestry for a block. Sends a request for header and justification for the given @@ -2076,7 +2115,7 @@ fn ancestry_request(block: NumberFor) -> BlockRequest { from: message::FromBlock::Number(block), to: None, direction: message::Direction::Ascending, - max: Some(1) + max: Some(1), } } @@ -2110,7 +2149,7 @@ fn handle_ancestor_search_state( let next_distance_to_tip = *next_distance_to_tip; if block_hash_match && next_distance_to_tip == One::one() { // We found the ancestor in the first step so there is no need to execute binary search. - return None; + return None } if block_hash_match { let left = curr_block_num; @@ -2118,15 +2157,18 @@ fn handle_ancestor_search_state( let middle = left + (right - left) / two; Some((AncestorSearchState::BinarySearch(left, right), middle)) } else { - let next_block_num = curr_block_num.checked_sub(&next_distance_to_tip) - .unwrap_or_else(Zero::zero); + let next_block_num = + curr_block_num.checked_sub(&next_distance_to_tip).unwrap_or_else(Zero::zero); let next_distance_to_tip = next_distance_to_tip * two; - Some((AncestorSearchState::ExponentialBackoff(next_distance_to_tip), next_block_num)) + Some(( + AncestorSearchState::ExponentialBackoff(next_distance_to_tip), + next_block_num, + )) } - } + }, AncestorSearchState::BinarySearch(mut left, mut right) => { if left >= curr_block_num { - return None; + return None } if block_hash_match { left = curr_block_num; @@ -2136,7 +2178,7 @@ fn handle_ancestor_search_state( assert!(right >= left); let middle = left + (right - left) / two; Some((AncestorSearchState::BinarySearch(left, right), middle)) - } + }, } } @@ -2152,7 +2194,7 @@ fn peer_block_request( ) -> Option<(Range>, BlockRequest)> { if best_num >= peer.best_number { // Will be downloaded as alternative fork instead. - return None; + return None } else if peer.common_number < finalized { trace!( target: "sync", @@ -2184,7 +2226,7 @@ fn peer_block_request( from, to: None, direction: message::Direction::Descending, - max: Some((range.end - range.start).saturated_into::()) + max: Some((range.end - range.start).saturated_into::()), }; Some((range, request)) @@ -2202,11 +2244,11 @@ fn fork_sync_request( targets.retain(|hash, r| { if r.number <= finalized { trace!(target: "sync", "Removed expired fork sync request {:?} (#{})", hash, r.number); - return false; + return false } if check_block(hash) != BlockStatus::Unknown { trace!(target: "sync", "Removed obsolete fork sync request {:?} (#{})", hash, r.number); - return false; + return false } true }); @@ -2223,27 +2265,34 @@ fn fork_sync_request( 1 }; trace!(target: "sync", "Downloading requested fork {:?} from {}, {} blocks", hash, id, count); - return Some((hash.clone(), message::generic::BlockRequest { - id: 0, - fields: attributes.clone(), - from: message::FromBlock::Hash(hash.clone()), - to: None, - direction: message::Direction::Descending, - max: Some(count), - })) + return Some(( + hash.clone(), + message::generic::BlockRequest { + id: 0, + fields: attributes.clone(), + from: message::FromBlock::Hash(hash.clone()), + to: None, + direction: message::Direction::Descending, + max: Some(count), + }, + )) } } None } /// Returns `true` if the given `block` is a descendent of `base`. -fn is_descendent_of(client: &T, base: &Block::Hash, block: &Block::Hash) -> sp_blockchain::Result - where - Block: BlockT, - T: HeaderMetadata + ?Sized, +fn is_descendent_of( + client: &T, + base: &Block::Hash, + block: &Block::Hash, +) -> sp_blockchain::Result +where + Block: BlockT, + T: HeaderMetadata + ?Sized, { if base == block { - return Ok(false); + return Ok(false) } let ancestor = sp_blockchain::lowest_common_ancestor(client, *block, *base)?; @@ -2276,13 +2325,13 @@ fn validate_blocks( blocks.last() } else { blocks.first() - }.and_then(|b| b.header.as_ref()); + } + .and_then(|b| b.header.as_ref()); - let expected_block = block_header.as_ref() - .map_or(false, |h| match request.from { - message::FromBlock::Hash(hash) => h.hash() == hash, - message::FromBlock::Number(n) => h.number() == &n, - }); + let expected_block = block_header.as_ref().map_or(false, |h| match request.from { + message::FromBlock::Hash(hash) => h.hash() == hash, + message::FromBlock::Number(n) => h.number() == &n, + }); if !expected_block { debug!( @@ -2295,8 +2344,8 @@ fn validate_blocks( return Err(BadPeer(who.clone(), rep::NOT_REQUESTED)) } - if request.fields.contains(message::BlockAttributes::HEADER) - && blocks.iter().any(|b| b.header.is_none()) + if request.fields.contains(message::BlockAttributes::HEADER) && + blocks.iter().any(|b| b.header.is_none()) { trace!( target: "sync", @@ -2307,8 +2356,8 @@ fn validate_blocks( return Err(BadPeer(who.clone(), rep::BAD_RESPONSE)) } - if request.fields.contains(message::BlockAttributes::BODY) - && blocks.iter().any(|b| b.body.is_none()) + if request.fields.contains(message::BlockAttributes::BODY) && + blocks.iter().any(|b| b.body.is_none()) { trace!( target: "sync", @@ -2336,7 +2385,8 @@ fn validate_blocks( } if let (Some(header), Some(body)) = (&b.header, &b.body) { let expected = *header.extrinsics_root(); - let got = HashFor::::ordered_trie_root(body.iter().map(Encode::encode).collect()); + let got = + HashFor::::ordered_trie_root(body.iter().map(Encode::encode).collect()); if expected != got { debug!( target:"sync", @@ -2356,17 +2406,19 @@ fn validate_blocks( #[cfg(test)] mod test { - use super::message::{FromBlock, BlockState, BlockData}; - use super::*; + use super::{ + message::{BlockData, BlockState, FromBlock}, + *, + }; + use futures::{executor::block_on, future::poll_fn}; use sc_block_builder::BlockBuilderProvider; use sp_blockchain::HeaderBackend; use sp_consensus::block_validation::DefaultBlockAnnounceValidator; use substrate_test_runtime_client::{ runtime::{Block, Hash, Header}, - ClientBlockImportExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, - BlockBuilderExt, TestClient, ClientExt, + BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClient, + TestClientBuilder, TestClientBuilderExt, }; - use futures::{future::poll_fn, executor::block_on}; #[test] fn processes_empty_response_on_justification_request_for_unknown_block() { @@ -2378,13 +2430,9 @@ mod test { let block_announce_validator = Box::new(DefaultBlockAnnounceValidator); let peer_id = PeerId::random(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - block_announce_validator, - 1, - None, - ).unwrap(); + let mut sync = + ChainSync::new(SyncMode::Full, client.clone(), block_announce_validator, 1, None) + .unwrap(); let (a1_hash, a1_number) = { let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; @@ -2398,50 +2446,36 @@ mod test { sync.request_justification(&a1_hash, a1_number); // the justification request should be scheduled to that peer - assert!( - sync.justification_requests().any(|(who, request)| { - who == peer_id && request.from == FromBlock::Hash(a1_hash) - }) - ); + assert!(sync + .justification_requests() + .any(|(who, request)| { who == peer_id && request.from == FromBlock::Hash(a1_hash) })); // there are no extra pending requests - assert_eq!( - sync.extra_justifications.pending_requests().count(), - 0, - ); + assert_eq!(sync.extra_justifications.pending_requests().count(), 0,); // there's one in-flight extra request to the expected peer - assert!( - sync.extra_justifications.active_requests().any(|(who, (hash, number))| { - *who == peer_id && *hash == a1_hash && *number == a1_number - }) - ); + assert!(sync.extra_justifications.active_requests().any(|(who, (hash, number))| { + *who == peer_id && *hash == a1_hash && *number == a1_number + })); // if the peer replies with an empty response (i.e. it doesn't know the block), // the active request should be cleared. assert_eq!( sync.on_block_justification( peer_id.clone(), - BlockResponse:: { - id: 0, - blocks: vec![], - } + BlockResponse:: { id: 0, blocks: vec![] } ), Ok(OnBlockJustification::Nothing), ); // there should be no in-flight requests - assert_eq!( - sync.extra_justifications.active_requests().count(), - 0, - ); + assert_eq!(sync.extra_justifications.active_requests().count(), 0,); // and the request should now be pending again, waiting for reschedule - assert!( - sync.extra_justifications.pending_requests().any(|(hash, number)| { - *hash == a1_hash && *number == a1_number - }) - ); + assert!(sync + .extra_justifications + .pending_requests() + .any(|(hash, number)| { *hash == a1_hash && *number == a1_number })); } #[test] @@ -2453,7 +2487,8 @@ mod test { Box::new(DefaultBlockAnnounceValidator), 1, None, - ).unwrap(); + ) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -2488,10 +2523,10 @@ mod test { // the justification request should be scheduled to the // new peer which is at the given block assert!(sync.justification_requests().any(|(p, r)| { - p == peer_id3 - && r.fields == BlockAttributes::JUSTIFICATION - && r.from == message::FromBlock::Hash(b1_hash) - && r.to == None + p == peer_id3 && + r.fields == BlockAttributes::JUSTIFICATION && + r.from == message::FromBlock::Hash(b1_hash) && + r.to == None })); assert_eq!( @@ -2503,7 +2538,9 @@ mod test { let block_requests = sync.restart(); // which should make us send out block requests to the first two peers - assert!(block_requests.map(|r| r.unwrap()).all(|(p, _)| { p == peer_id1 || p == peer_id2 })); + assert!(block_requests + .map(|r| r.unwrap()) + .all(|(p, _)| { p == peer_id1 || p == peer_id2 })); // peer 3 should be unaffected it was downloading finality data assert_eq!( @@ -2514,30 +2551,18 @@ mod test { // Set common block to something that we don't have (e.g. failed import) sync.peers.get_mut(&peer_id3).unwrap().common_number = 100; let _ = sync.restart().count(); - assert_eq!( - sync.peers.get(&peer_id3).unwrap().common_number, - 50 - ); + assert_eq!(sync.peers.get(&peer_id3).unwrap().common_number, 50); } /// Send a block annoucnement for the given `header`. - fn send_block_announce( - header: Header, - peer_id: &PeerId, - sync: &mut ChainSync, - ) { + fn send_block_announce(header: Header, peer_id: &PeerId, sync: &mut ChainSync) { let block_annnounce = BlockAnnounce { header: header.clone(), state: Some(BlockState::Best), data: Some(Vec::new()), }; - sync.push_block_announce_validation( - peer_id.clone(), - header.hash(), - block_annnounce, - true, - ); + sync.push_block_announce_validation(peer_id.clone(), header.hash(), block_annnounce, true); // Poll until we have procssed the block announcement block_on(poll_fn(|cx| loop { @@ -2551,8 +2576,9 @@ mod test { fn create_block_response(blocks: Vec) -> BlockResponse { BlockResponse:: { id: 0, - blocks: blocks.into_iter().map(|b| - BlockData:: { + blocks: blocks + .into_iter() + .map(|b| BlockData:: { hash: b.hash(), header: Some(b.header().clone()), body: Some(b.deconstruct().1), @@ -2561,8 +2587,8 @@ mod test { message_queue: None, justification: None, justifications: None, - } - ).collect(), + }) + .collect(), } } @@ -2591,11 +2617,8 @@ mod test { fn build_block(client: &mut Arc, at: Option, fork: bool) -> Block { let at = at.unwrap_or_else(|| client.info().best_hash); - let mut block_builder = client.new_block_at( - &BlockId::Hash(at), - Default::default(), - false, - ).unwrap(); + let mut block_builder = + client.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); if fork { block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); @@ -2628,15 +2651,16 @@ mod test { Box::new(DefaultBlockAnnounceValidator), 5, None, - ).unwrap(); + ) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); let mut client2 = client.clone(); let mut build_block_at = |at, import| { - let mut block_builder = client2.new_block_at(&BlockId::Hash(at), Default::default(), false) - .unwrap(); + let mut block_builder = + client2.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); // Make sure we generate a different block as fork block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); @@ -2695,13 +2719,11 @@ mod test { let response = create_block_response(vec![block2.clone()]); let res = sync.on_block_data(&peer_id1, Some(request2), response).unwrap(); - assert!( - matches!( - res, - OnBlockData::Import(_, blocks) - if blocks.iter().all(|b| [2, 3, 4].contains(b.header.as_ref().unwrap().number())) - ) - ); + assert!(matches!( + res, + OnBlockData::Import(_, blocks) + if blocks.iter().all(|b| [2, 3, 4].contains(b.header.as_ref().unwrap().number())) + )); let response = create_block_response(vec![block2.clone()]); let res = sync.on_block_data(&peer_id2, Some(request3), response).unwrap(); @@ -2730,7 +2752,9 @@ mod test { let blocks = { let mut client = Arc::new(TestClientBuilder::new().build()); - (0..MAX_DOWNLOAD_AHEAD * 2).map(|_| build_block(&mut client, None, false)).collect::>() + (0..MAX_DOWNLOAD_AHEAD * 2) + .map(|_| build_block(&mut client, None, false)) + .collect::>() }; let mut client = Arc::new(TestClientBuilder::new().build()); @@ -2742,14 +2766,16 @@ mod test { Box::new(DefaultBlockAnnounceValidator), 5, None, - ).unwrap(); + ) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); let best_block = blocks.last().unwrap().clone(); // Connect the node we will sync from - sync.new_peer(peer_id1.clone(), best_block.hash(), *best_block.header().number()).unwrap(); + sync.new_peer(peer_id1.clone(), best_block.hash(), *best_block.header().number()) + .unwrap(); sync.new_peer(peer_id2.clone(), info.best_hash, 0).unwrap(); let mut best_block_num = 0; @@ -2769,18 +2795,17 @@ mod test { let response = create_block_response(resp_blocks.clone()); let res = sync.on_block_data(&peer_id1, Some(request), response).unwrap(); - assert!( - matches!( - res, - OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST - ), - ); + assert!(matches!( + res, + OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST + ),); best_block_num += MAX_BLOCKS_TO_REQUEST as u32; - resp_blocks.into_iter() - .rev() - .for_each(|b| block_on(client.import_as_final(BlockOrigin::Own, b)).unwrap()); + resp_blocks + .into_iter() + .rev() + .for_each(|b| block_on(client.import_as_final(BlockOrigin::Own, b)).unwrap()); } // "Wait" for the queue to clear @@ -2806,12 +2831,10 @@ mod test { let response = create_block_response(vec![blocks[(best_block_num - 1) as usize].clone()]); let res = sync.on_block_data(&peer_id2, Some(peer2_req), response).unwrap(); - assert!( - matches!( - res, - OnBlockData::Import(_, blocks) if blocks.is_empty() - ), - ); + assert!(matches!( + res, + OnBlockData::Import(_, blocks) if blocks.is_empty() + ),); let peer1_from = unwrap_from_block_number(peer1_req.unwrap().from); @@ -2850,10 +2873,13 @@ mod test { .cloned() .collect::>(); - fork_blocks.into_iter().chain( + fork_blocks + .into_iter() + .chain( (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1) - .map(|_| build_block(&mut client, None, true)) - ).collect::>() + .map(|_| build_block(&mut client, None, true)), + ) + .collect::>() }; let info = client.info(); @@ -2864,27 +2890,27 @@ mod test { Box::new(DefaultBlockAnnounceValidator), 5, None, - ).unwrap(); + ) + .unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); let just = (*b"TEST", Vec::new()); - client.finalize_block(BlockId::Hash(finalized_block.hash()), Some(just)).unwrap(); + client + .finalize_block(BlockId::Hash(finalized_block.hash()), Some(just)) + .unwrap(); sync.update_chain_info(&info.best_hash, info.best_number); let peer_id1 = PeerId::random(); let common_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize / 2].clone(); // Connect the node we will sync from - sync.new_peer(peer_id1.clone(), common_block.hash(), *common_block.header().number()).unwrap(); + sync.new_peer(peer_id1.clone(), common_block.hash(), *common_block.header().number()) + .unwrap(); send_block_announce(fork_blocks.last().unwrap().header().clone(), &peer_id1, &mut sync); - let mut request = get_block_request( - &mut sync, - FromBlock::Number(info.best_number), - 1, - &peer_id1, - ); + let mut request = + get_block_request(&mut sync, FromBlock::Number(info.best_number), 1, &peer_id1); // Do the ancestor search loop { @@ -2919,36 +2945,34 @@ mod test { let response = create_block_response(resp_blocks.clone()); let res = sync.on_block_data(&peer_id1, Some(request), response).unwrap(); - assert!( - matches!( - res, - OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST - ), - ); + assert!(matches!( + res, + OnBlockData::Import(_, blocks) if blocks.len() == MAX_BLOCKS_TO_REQUEST + ),); best_block_num += MAX_BLOCKS_TO_REQUEST as u32; let _ = sync.on_blocks_processed( MAX_BLOCKS_TO_REQUEST as usize, MAX_BLOCKS_TO_REQUEST as usize, - resp_blocks.iter() + resp_blocks + .iter() .rev() - .map(|b| + .map(|b| { ( - Ok( - BlockImportResult::ImportedUnknown( - b.header().number().clone(), - Default::default(), - Some(peer_id1.clone()), - ) - ), + Ok(BlockImportResult::ImportedUnknown( + b.header().number().clone(), + Default::default(), + Some(peer_id1.clone()), + )), b.hash(), ) - ) - .collect() + }) + .collect(), ); - resp_blocks.into_iter() + resp_blocks + .into_iter() .rev() .for_each(|b| block_on(client.import(BlockOrigin::Own, b)).unwrap()); } @@ -2966,9 +2990,7 @@ mod test { fn removes_target_fork_on_disconnect() { sp_tracing::try_init_simple(); let mut client = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..3) - .map(|_| build_block(&mut client, None, false)) - .collect::>(); + let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); let mut sync = ChainSync::new( SyncMode::Full, @@ -2976,12 +2998,14 @@ mod test { Box::new(DefaultBlockAnnounceValidator), 1, None, - ).unwrap(); + ) + .unwrap(); let peer_id1 = PeerId::random(); let common_block = blocks[1].clone(); // Connect the node we will sync from - sync.new_peer(peer_id1.clone(), common_block.hash(), *common_block.header().number()).unwrap(); + sync.new_peer(peer_id1.clone(), common_block.hash(), *common_block.header().number()) + .unwrap(); // Create a "new" header and announce it let mut header = blocks[0].header().clone(); diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 2284cfe9e564d..51a49c3764bae 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -16,26 +16,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::sync::Arc; -use sp_runtime::traits::{Block as BlockT, NumberFor, Header, Zero}; -use sp_finality_grandpa::{SetId, AuthorityList}; -use crate::schema::v1::{StateRequest, StateResponse}; -use crate::chain::Client; +pub use super::state::ImportResult; use super::state::StateSync; -use crate::{WarpSyncProgress, WarpSyncPhase}; pub use crate::warp_request_handler::{ - Request as WarpProofRequest, WarpSyncProvider, EncodedProof, VerificationResult, + EncodedProof, Request as WarpProofRequest, VerificationResult, WarpSyncProvider, }; -pub use super::state::ImportResult; +use crate::{ + chain::Client, + schema::v1::{StateRequest, StateResponse}, + WarpSyncPhase, WarpSyncProgress, +}; +use sp_finality_grandpa::{AuthorityList, SetId}; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; +use std::sync::Arc; /// Warp sync support. enum Phase { - WarpProof { - set_id: SetId, - authorities: AuthorityList, - last_hash: B::Hash, - }, + WarpProof { set_id: SetId, authorities: AuthorityList, last_hash: B::Hash }, State(StateSync), } @@ -65,8 +63,7 @@ impl WarpSync { client: Arc>, warp_sync_provider: Arc>, ) -> Self { - let last_hash = client.hash(Zero::zero()).unwrap() - .expect("Genesis header always exists"); + let last_hash = client.hash(Zero::zero()).unwrap().expect("Genesis header always exists"); let phase = Phase::WarpProof { set_id: 0, authorities: warp_sync_provider.current_authorities(), @@ -85,11 +82,11 @@ impl WarpSync { /// Validate and import a state reponse. pub fn import_state(&mut self, response: StateResponse) -> ImportResult { match &mut self.phase { - Phase::WarpProof {..} => { + Phase::WarpProof { .. } => { log::debug!(target: "sync", "Unexpected state response"); - return ImportResult::BadResponse; - } - Phase::State(sync) => sync.import(response) + return ImportResult::BadResponse + }, + Phase::State(sync) => sync.import(response), } } @@ -108,7 +105,7 @@ impl WarpSync { ) { Err(e) => { log::debug!(target: "sync", "Bad warp proof response: {:?}", e); - return WarpProofImportResult::BadResponse; + return WarpProofImportResult::BadResponse }, Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => { log::debug!(target: "sync", "Verified partial proof, set_id={:?}", new_set_id); @@ -117,10 +114,10 @@ impl WarpSync { *last_hash = new_last_hash.clone(); self.total_proof_bytes += response.0.len() as u64; WarpProofImportResult::WarpProofRequest(WarpProofRequest { - begin: new_last_hash + begin: new_last_hash, }) }, - Ok(VerificationResult::Complete(new_set_id, _, header)) => { + Ok(VerificationResult::Complete(new_set_id, _, header)) => { log::debug!(target: "sync", "Verified complete proof, set_id={:?}", new_set_id); self.target_hash = header.hash(); self.target_num = *header.number(); @@ -129,17 +126,17 @@ impl WarpSync { let request = state_sync.next_request(); self.phase = Phase::State(state_sync); WarpProofImportResult::StateRequest(request) - } + }, } - } + }, } } /// Produce next state request. pub fn next_state_request(&self) -> Option { match &self.phase { - Phase::WarpProof {..} => None, - Phase::State(sync) => Some(sync.next_request()) + Phase::WarpProof { .. } => None, + Phase::State(sync) => Some(sync.next_request()), } } @@ -147,11 +144,8 @@ impl WarpSync { pub fn next_warp_poof_request(&self) -> Option> { match &self.phase { Phase::State(_) => None, - Phase::WarpProof { last_hash, .. } => { - Some(WarpProofRequest { - begin: last_hash.clone(), - }) - } + Phase::WarpProof { last_hash, .. } => + Some(WarpProofRequest { begin: last_hash.clone() }), } } @@ -168,7 +162,7 @@ impl WarpSync { /// Check if the state is complete. pub fn is_complete(&self) -> bool { match &self.phase { - Phase::WarpProof {..} => false, + Phase::WarpProof { .. } => false, Phase::State(sync) => sync.is_complete(), } } @@ -176,7 +170,7 @@ impl WarpSync { /// Returns state sync estimated progress (percentage, bytes) pub fn progress(&self) -> WarpSyncProgress { match &self.phase { - Phase::WarpProof {..} => WarpSyncProgress { + Phase::WarpProof { .. } => WarpSyncProgress { phase: WarpSyncPhase::DownloadingWarpProofs, total_bytes: self.total_proof_bytes, }, @@ -187,8 +181,7 @@ impl WarpSync { WarpSyncPhase::DownloadingState }, total_bytes: self.total_proof_bytes + sync.progress().size, - } + }, } } } - diff --git a/client/network/src/service/tests.rs b/client/network/src/service/tests.rs index 2d3daed322b11..a45c998ed81d4 100644 --- a/client/network/src/service/tests.rs +++ b/client/network/src/service/tests.rs @@ -58,7 +58,8 @@ fn build_test_full_node( ), String, > { - let maybe_keys = block.header + let maybe_keys = block + .header .digest() .log(|l| { l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura")) diff --git a/client/network/src/warp_request_handler.rs b/client/network/src/warp_request_handler.rs index 172ab5c3551e7..31a6a2d4f60cb 100644 --- a/client/network/src/warp_request_handler.rs +++ b/client/network/src/warp_request_handler.rs @@ -16,15 +16,16 @@ //! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer. -use codec::{Encode, Decode}; use crate::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig}; -use futures::channel::{mpsc, oneshot}; -use futures::stream::StreamExt; +use codec::{Decode, Encode}; +use futures::{ + channel::{mpsc, oneshot}, + stream::StreamExt, +}; use log::debug; +use sp_finality_grandpa::{AuthorityList, SetId}; use sp_runtime::traits::Block as BlockT; -use sp_finality_grandpa::{SetId, AuthorityList}; -use std::time::Duration; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; /// Scale-encoded warp sync proof response. pub struct EncodedProof(pub Vec); @@ -98,13 +99,7 @@ impl RequestHandler { let mut request_response_config = generate_request_response_config(protocol_id); request_response_config.inbound_queue = Some(tx); - ( - Self { - backend, - request_receiver, - }, - request_response_config, - ) + (Self { backend, request_receiver }, request_response_config) } fn handle_request( @@ -114,13 +109,18 @@ impl RequestHandler { ) -> Result<(), HandleRequestError> { let request = Request::::decode(&mut &payload[..])?; - let EncodedProof(proof) = self.backend.generate(request.begin).map_err(HandleRequestError::InvalidRequest)?; - - pending_response.send(OutgoingResponse { - result: Ok(proof), - reputation_changes: Vec::new(), - sent_feedback: None, - }).map_err(|_| HandleRequestError::SendResponse) + let EncodedProof(proof) = self + .backend + .generate(request.begin) + .map_err(HandleRequestError::InvalidRequest)?; + + pending_response + .send(OutgoingResponse { + result: Ok(proof), + reputation_changes: Vec::new(), + sent_feedback: None, + }) + .map_err(|_| HandleRequestError::SendResponse) } /// Run [`RequestHandler`]. @@ -129,7 +129,8 @@ impl RequestHandler { let IncomingRequest { peer, payload, pending_response } = request; match self.handle_request(payload, pending_response) { - Ok(()) => debug!(target: "sync", "Handled grandpa warp sync request from {}.", peer), + Ok(()) => + debug!(target: "sync", "Handled grandpa warp sync request from {}.", peer), Err(e) => debug!( target: "sync", "Failed to handle grandpa warp sync request from {}: {}", diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 9c468482250ae..73cf4e7541245 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -23,52 +23,58 @@ mod block_import; mod sync; use std::{ - borrow::Cow, collections::HashMap, pin::Pin, sync::Arc, task::{Poll, Context as FutureContext} + borrow::Cow, + collections::HashMap, + pin::Pin, + sync::Arc, + task::{Context as FutureContext, Poll}, }; -use libp2p::build_multiaddr; +use futures::{future::BoxFuture, prelude::*}; +use libp2p::{build_multiaddr, PeerId}; use log::trace; -use sc_network::block_request_handler::{self, BlockRequestHandler}; -use sc_network::state_request_handler::{self, StateRequestHandler}; -use sc_network::light_client_requests::{self, handler::LightClientRequestHandler}; -use sp_blockchain::{ - HeaderBackend, Result as ClientResult, - well_known_cache_keys::{self, Id as CacheKeyId}, - Info as BlockchainInfo, -}; +use parking_lot::Mutex; +use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; use sc_client_api::{ - BlockchainEvents, BlockImportNotification, FinalityNotifications, ImportNotifications, FinalityNotification, - backend::{TransactionFor, AuxStore, Backend, Finalizer}, BlockBackend, + backend::{AuxStore, Backend, Finalizer, TransactionFor}, + BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, + FinalityNotifications, ImportNotifications, }; use sc_consensus::LongestChain; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; -use sc_network::config::Role; -use sp_consensus::block_validation::{DefaultBlockAnnounceValidator, BlockAnnounceValidator}; -use sp_consensus::import_queue::{ - BasicQueue, BoxJustificationImport, Verifier, -}; -use sp_consensus::block_import::{BlockImport, ImportResult}; -use sp_consensus::Error as ConsensusError; -use sp_consensus::{BlockOrigin, ForkChoiceStrategy, BlockImportParams, BlockCheckParams, JustificationImport}; -use futures::prelude::*; -use futures::future::BoxFuture; +pub use sc_network::config::EmptyTransactionPool; use sc_network::{ - NetworkWorker, NetworkService, config::{ProtocolId, MultiaddrWithPeerId, NonReservedPeerMode}, - Multiaddr, + block_request_handler::{self, BlockRequestHandler}, + config::{ + MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode, + ProtocolConfig, ProtocolId, Role, SyncMode, TransportConfig, + }, + light_client_requests::{self, handler::LightClientRequestHandler}, + state_request_handler::{self, StateRequestHandler}, + Multiaddr, NetworkService, NetworkWorker, +}; +use sc_service::client::Client; +use sp_blockchain::{ + well_known_cache_keys::{self, Id as CacheKeyId}, + HeaderBackend, Info as BlockchainInfo, Result as ClientResult, +}; +use sp_consensus::{ + block_import::{BlockImport, ImportResult}, + block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator}, + import_queue::{BasicQueue, BoxJustificationImport, Verifier}, + BlockCheckParams, BlockImportParams, BlockOrigin, Error as ConsensusError, ForkChoiceStrategy, + JustificationImport, }; -use sc_network::config::{NetworkConfiguration, NonDefaultSetConfig, TransportConfig, SyncMode}; -use libp2p::PeerId; -use parking_lot::Mutex; use sp_core::H256; -use sc_network::config::ProtocolConfig; -use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use sp_runtime::{Justification, Justifications}; +use sp_runtime::{ + generic::{BlockId, OpaqueDigestItemId}, + traits::{Block as BlockT, Header as HeaderT, NumberFor}, + Justification, Justifications, +}; use substrate_test_runtime_client::AccountKeyring; -use sc_service::client::Client; -pub use sc_network::config::EmptyTransactionPool; -pub use substrate_test_runtime_client::runtime::{Block, Extrinsic, Hash, Transfer}; -pub use substrate_test_runtime_client::{TestClient, TestClientBuilder, TestClientBuilderExt}; +pub use substrate_test_runtime_client::{ + runtime::{Block, Extrinsic, Hash, Transfer}, + TestClient, TestClientBuilder, TestClientBuilderExt, +}; type AuthorityId = sp_consensus_babe::AuthorityId; @@ -85,10 +91,7 @@ impl PassThroughVerifier { /// /// Every verified block will use `finalized` for the `BlockImportParams`. pub fn new(finalized: bool) -> Self { - Self { - finalized, - fork_choice: ForkChoiceStrategy::LongestChain, - } + Self { finalized, fork_choice: ForkChoiceStrategy::LongestChain } } /// Create a new instance. @@ -96,10 +99,7 @@ impl PassThroughVerifier { /// Every verified block will use `finalized` for the `BlockImportParams` and /// the given [`ForkChoiceStrategy`]. pub fn new_with_fork_choice(finalized: bool, fork_choice: ForkChoiceStrategy) -> Self { - Self { - finalized, - fork_choice, - } + Self { finalized, fork_choice } } } @@ -110,10 +110,13 @@ impl Verifier for PassThroughVerifier { &mut self, mut block: BlockImportParams, ) -> Result<(BlockImportParams, Option)>>), String> { - let maybe_keys = block.header.digest() - .log(|l| l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura")) - .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) - ) + let maybe_keys = block + .header + .digest() + .log(|l| { + l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura")) + .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) + }) .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); block.finalized = self.finalized; block.fork_choice = Some(self.fork_choice.clone()); @@ -125,13 +128,13 @@ pub type PeersFullClient = Client< substrate_test_runtime_client::Backend, substrate_test_runtime_client::Executor, Block, - substrate_test_runtime_client::runtime::RuntimeApi + substrate_test_runtime_client::runtime::RuntimeApi, >; pub type PeersLightClient = Client< substrate_test_runtime_client::LightBackend, substrate_test_runtime_client::LightExecutor, Block, - substrate_test_runtime_client::runtime::RuntimeApi + substrate_test_runtime_client::runtime::RuntimeApi, >; #[derive(Clone)] @@ -166,7 +169,10 @@ impl PeersClient { } } - pub fn header(&self, block: &BlockId) -> ClientResult::Header>> { + pub fn header( + &self, + block: &BlockId, + ) -> ClientResult::Header>> { match *self { PeersClient::Full(ref client, ref _backend) => client.header(block), PeersClient::Light(ref client, ref _backend) => client.header(block), @@ -200,7 +206,7 @@ impl PeersClient { } } - pub fn import_notification_stream(&self) -> ImportNotifications{ + pub fn import_notification_stream(&self) -> ImportNotifications { match *self { PeersClient::Full(ref client, ref _backend) => client.import_notification_stream(), PeersClient::Light(ref client, ref _backend) => client.import_notification_stream(), @@ -211,11 +217,13 @@ impl PeersClient { &self, id: BlockId, justification: Option, - notify: bool + notify: bool, ) -> ClientResult<()> { match *self { - PeersClient::Full(ref client, ref _backend) => client.finalize_block(id, justification, notify), - PeersClient::Light(ref client, ref _backend) => client.finalize_block(id, justification, notify), + PeersClient::Full(ref client, ref _backend) => + client.finalize_block(id, justification, notify), + PeersClient::Light(ref client, ref _backend) => + client.finalize_block(id, justification, notify), } } } @@ -266,7 +274,8 @@ pub struct Peer { listen_addr: Multiaddr, } -impl Peer where +impl Peer +where B: BlockImport + Send + Sync, B::Transaction: Send, { @@ -281,7 +290,9 @@ impl Peer where } // Returns a clone of the local SelectChain, only available on full nodes - pub fn select_chain(&self) -> Option> { + pub fn select_chain( + &self, + ) -> Option> { self.select_chain.clone() } @@ -321,17 +332,22 @@ impl Peer where } /// Add blocks to the peer -- edit the block before adding - pub fn generate_blocks( - &mut self, - count: usize, - origin: BlockOrigin, - edit_block: F, - ) -> H256 - where - F: FnMut(BlockBuilder) -> Block + pub fn generate_blocks(&mut self, count: usize, origin: BlockOrigin, edit_block: F) -> H256 + where + F: FnMut( + BlockBuilder, + ) -> Block, { let best_hash = self.client.info().best_hash; - self.generate_blocks_at(BlockId::Hash(best_hash), count, origin, edit_block, false, true, true) + self.generate_blocks_at( + BlockId::Hash(best_hash), + count, + origin, + edit_block, + false, + true, + true, + ) } /// Add blocks to the peer -- edit the block before adding. The chain will @@ -345,16 +361,18 @@ impl Peer where headers_only: bool, inform_sync_about_new_best_block: bool, announce_block: bool, - ) -> H256 where F: FnMut(BlockBuilder) -> Block { - let full_client = self.client.as_full() - .expect("blocks could only be generated by full clients"); + ) -> H256 + where + F: FnMut( + BlockBuilder, + ) -> Block, + { + let full_client = + self.client.as_full().expect("blocks could only be generated by full clients"); let mut at = full_client.header(&at).unwrap().unwrap().hash(); - for _ in 0..count { - let builder = full_client.new_block_at( - &BlockId::Hash(at), - Default::default(), - false, - ).unwrap(); + for _ in 0..count { + let builder = + full_client.new_block_at(&BlockId::Hash(at), Default::default(), false).unwrap(); let block = edit_block(builder); let hash = block.header.hash(); trace!( @@ -367,18 +385,16 @@ impl Peer where let header = block.header.clone(); let mut import_block = BlockImportParams::new(origin, header.clone()); import_block.body = if headers_only { None } else { Some(block.extrinsics) }; - let (import_block, cache) = futures::executor::block_on(self.verifier.verify( - import_block, - )).unwrap(); + let (import_block, cache) = + futures::executor::block_on(self.verifier.verify(import_block)).unwrap(); let cache = if let Some(cache) = cache { cache.into_iter().collect() } else { Default::default() }; - futures::executor::block_on( - self.block_import.import_block(import_block, cache) - ).expect("block_import failed"); + futures::executor::block_on(self.block_import.import_block(import_block, cache)) + .expect("block_import failed"); if announce_block { self.network.service().announce_block(hash, None); } @@ -450,7 +466,8 @@ impl Peer where self.generate_blocks_at( at, count, - BlockOrigin::File, |mut builder| { + BlockOrigin::File, + |mut builder| { let transfer = Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Alice.into(), @@ -512,9 +529,10 @@ impl Peer where /// Count the total number of imported blocks. pub fn blocks_count(&self) -> u64 { - self.backend.as_ref().map( - |backend| backend.blockchain().info().best_number - ).unwrap_or(0) + self.backend + .as_ref() + .map(|backend| backend.blockchain().info().best_number) + .unwrap_or(0) } /// Return a collection of block hashes that failed verification @@ -523,9 +541,10 @@ impl Peer where } pub fn has_block(&self, hash: &H256) -> bool { - self.backend.as_ref().map( - |backend| backend.blockchain().header(BlockId::hash(*hash)).unwrap().is_some() - ).unwrap_or(false) + self.backend + .as_ref() + .map(|backend| backend.blockchain().header(BlockId::hash(*hash)).unwrap().is_some()) + .unwrap_or(false) } } @@ -534,22 +553,22 @@ pub trait BlockImportAdapterFull: Block, Transaction = TransactionFor, Error = ConsensusError, - > + - Send + - Sync + - Clone -{} + > + Send + + Sync + + Clone +{ +} impl BlockImportAdapterFull for T where T: BlockImport< - Block, - Transaction = TransactionFor, - Error = ConsensusError, - > + - Send + - Sync + - Clone -{} + Block, + Transaction = TransactionFor, + Error = ConsensusError, + > + Send + + Sync + + Clone +{ +} /// Implements `BlockImport` for any `Transaction`. Internally the transaction is /// "converted", aka the field is set to `None`. @@ -564,14 +583,13 @@ pub struct BlockImportAdapter { impl BlockImportAdapter { /// Create a new instance of `Self::Full`. pub fn new(inner: I) -> Self { - Self { - inner, - } + Self { inner } } } #[async_trait::async_trait] -impl BlockImport for BlockImportAdapter where +impl BlockImport for BlockImportAdapter +where I: BlockImport + Send + Sync, I::Transaction: Send, { @@ -653,7 +671,10 @@ pub struct FullPeerConfig { pub storage_chain: bool, } -pub trait TestNetFactory: Sized where >::Transaction: Send { +pub trait TestNetFactory: Sized +where + >::Transaction: Send, +{ type Verifier: 'static + Verifier; type BlockImport: BlockImport + Clone + Send + Sync + 'static; type PeerData: Default; @@ -676,12 +697,14 @@ pub trait TestNetFactory: Sized where >: ); /// Get custom block import handle for fresh client, along with peer data. - fn make_block_import(&self, client: PeersClient) - -> ( - BlockImportAdapter, - Option>, - Self::PeerData, - ); + fn make_block_import( + &self, + client: PeersClient, + ) -> ( + BlockImportAdapter, + Option>, + Self::PeerData, + ); fn default_config() -> ProtocolConfig { ProtocolConfig::default() @@ -712,18 +735,15 @@ pub trait TestNetFactory: Sized where >: (Some(keep_blocks), false) => TestClientBuilder::with_pruning_window(keep_blocks), (None, false) => TestClientBuilder::with_default_backend(), }; - if matches!(config.sync_mode, SyncMode::Fast{..}) { + if matches!(config.sync_mode, SyncMode::Fast { .. }) { test_client_builder = test_client_builder.set_no_genesis(); } let backend = test_client_builder.backend(); let (c, longest_chain) = test_client_builder.build_with_longest_chain(); let client = Arc::new(c); - let ( - block_import, - justification_import, - data, - ) = self.make_block_import(PeersClient::Full(client.clone(), backend.clone())); + let (block_import, justification_import, data) = + self.make_block_import(PeersClient::Full(client.clone(), backend.clone())); let verifier = self.make_verifier( PeersClient::Full(client.clone(), backend.clone()), @@ -742,30 +762,31 @@ pub trait TestNetFactory: Sized where >: let listen_addr = build_multiaddr![Memory(rand::random::())]; - let mut network_config = NetworkConfiguration::new( - "test-node", - "test-client", - Default::default(), - None, - ); + let mut network_config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); network_config.sync_mode = config.sync_mode; network_config.transport = TransportConfig::MemoryOnly; network_config.listen_addresses = vec![listen_addr.clone()]; network_config.allow_non_globals_in_dht = true; - network_config.extra_sets = config.notifications_protocols.into_iter().map(|p| { - NonDefaultSetConfig { + network_config.extra_sets = config + .notifications_protocols + .into_iter() + .map(|p| NonDefaultSetConfig { notifications_protocol: p, fallback_names: Vec::new(), max_notification_size: 1024 * 1024, - set_config: Default::default() - } - }).collect(); + set_config: Default::default(), + }) + .collect(); if let Some(connect_to) = config.connect_to_peers { - let addrs = connect_to.iter().map(|v| { - let peer_id = self.peer(*v).network_service().local_peer_id().clone(); - let multiaddr = self.peer(*v).listen_addr.clone(); - MultiaddrWithPeerId { peer_id, multiaddr } - }).collect(); + let addrs = connect_to + .iter() + .map(|v| { + let peer_id = self.peer(*v).network_service().local_peer_id().clone(); + let multiaddr = self.peer(*v).listen_addr.clone(); + MultiaddrWithPeerId { peer_id, multiaddr } + }) + .collect(); network_config.default_peers_set.reserved_nodes = addrs; network_config.default_peers_set.non_reserved_mode = NonReservedPeerMode::Deny; } @@ -773,27 +794,22 @@ pub trait TestNetFactory: Sized where >: let protocol_id = ProtocolId::from("test-protocol-name"); let block_request_protocol_config = { - let (handler, protocol_config) = BlockRequestHandler::new( - &protocol_id, - client.clone(), - 50, - ); + let (handler, protocol_config) = + BlockRequestHandler::new(&protocol_id, client.clone(), 50); self.spawn_task(handler.run().boxed()); protocol_config }; let state_request_protocol_config = { - let (handler, protocol_config) = StateRequestHandler::new( - &protocol_id, - client.clone(), - 50, - ); + let (handler, protocol_config) = + StateRequestHandler::new(&protocol_id, client.clone(), 50); self.spawn_task(handler.run().boxed()); protocol_config }; let light_client_request_protocol_config = { - let (handler, protocol_config) = LightClientRequestHandler::new(&protocol_id, client.clone()); + let (handler, protocol_config) = + LightClientRequestHandler::new(&protocol_id, client.clone()); self.spawn_task(handler.run().boxed()); protocol_config }; @@ -801,21 +817,25 @@ pub trait TestNetFactory: Sized where >: let network = NetworkWorker::new(sc_network::config::Params { role: if config.is_authority { Role::Authority } else { Role::Full }, executor: None, - transactions_handler_executor: Box::new(|task| { async_std::task::spawn(task); }), + transactions_handler_executor: Box::new(|task| { + async_std::task::spawn(task); + }), network_config, chain: client.clone(), on_demand: None, transaction_pool: Arc::new(EmptyTransactionPool), protocol_id, import_queue, - block_announce_validator: config.block_announce_validator + block_announce_validator: config + .block_announce_validator .unwrap_or_else(|| Box::new(DefaultBlockAnnounceValidator)), metrics_registry: None, block_request_protocol_config, state_request_protocol_config, light_client_request_protocol_config, warp_sync: None, - }).unwrap(); + }) + .unwrap(); trace!(target: "test_network", "Peer identifier: {}", network.service().local_peer_id()); @@ -828,7 +848,8 @@ pub trait TestNetFactory: Sized where >: } let imported_blocks_stream = Box::pin(client.import_notification_stream().fuse()); - let finality_notification_stream = Box::pin(client.finality_notification_stream().fuse()); + let finality_notification_stream = + Box::pin(client.finality_notification_stream().fuse()); peers.push(Peer { data, @@ -849,11 +870,8 @@ pub trait TestNetFactory: Sized where >: fn add_light_peer(&mut self) { let (c, backend) = substrate_test_runtime_client::new_light(); let client = Arc::new(c); - let ( - block_import, - justification_import, - data, - ) = self.make_block_import(PeersClient::Light(client.clone(), backend.clone())); + let (block_import, justification_import, data) = + self.make_block_import(PeersClient::Light(client.clone(), backend.clone())); let verifier = self.make_verifier( PeersClient::Light(client.clone(), backend.clone()), @@ -872,24 +890,18 @@ pub trait TestNetFactory: Sized where >: let listen_addr = build_multiaddr![Memory(rand::random::())]; - let mut network_config = NetworkConfiguration::new( - "test-node", - "test-client", - Default::default(), - None, - ); + let mut network_config = + NetworkConfiguration::new("test-node", "test-client", Default::default(), None); network_config.transport = TransportConfig::MemoryOnly; network_config.listen_addresses = vec![listen_addr.clone()]; network_config.allow_non_globals_in_dht = true; let protocol_id = ProtocolId::from("test-protocol-name"); - let block_request_protocol_config = block_request_handler::generate_protocol_config( - &protocol_id, - ); - let state_request_protocol_config = state_request_handler::generate_protocol_config( - &protocol_id, - ); + let block_request_protocol_config = + block_request_handler::generate_protocol_config(&protocol_id); + let state_request_protocol_config = + state_request_handler::generate_protocol_config(&protocol_id); let light_client_request_protocol_config = light_client_requests::generate_protocol_config(&protocol_id); @@ -897,7 +909,9 @@ pub trait TestNetFactory: Sized where >: let network = NetworkWorker::new(sc_network::config::Params { role: Role::Light, executor: None, - transactions_handler_executor: Box::new(|task| { async_std::task::spawn(task); }), + transactions_handler_executor: Box::new(|task| { + async_std::task::spawn(task); + }), network_config, chain: client.clone(), on_demand: None, @@ -910,15 +924,20 @@ pub trait TestNetFactory: Sized where >: state_request_protocol_config, light_client_request_protocol_config, warp_sync: None, - }).unwrap(); + }) + .unwrap(); self.mut_peers(|peers| { for peer in peers.iter_mut() { - peer.network.add_known_address(network.service().local_peer_id().clone(), listen_addr.clone()); + peer.network.add_known_address( + network.service().local_peer_id().clone(), + listen_addr.clone(), + ); } let imported_blocks_stream = Box::pin(client.import_notification_stream().fuse()); - let finality_notification_stream = Box::pin(client.finality_notification_stream().fuse()); + let finality_notification_stream = + Box::pin(client.finality_notification_stream().fuse()); peers.push(Peer { data, @@ -958,7 +977,7 @@ pub trait TestNetFactory: Sized where >: match (highest, peer.client.info().best_hash) { (None, b) => highest = Some(b), (Some(ref a), ref b) if a == b => {}, - (Some(_), _) => return Poll::Pending + (Some(_), _) => return Poll::Pending, } } Poll::Ready(()) @@ -999,23 +1018,27 @@ pub trait TestNetFactory: Sized where >: /// /// Calls `poll_until_sync` repeatedly. fn block_until_sync(&mut self) { - futures::executor::block_on(futures::future::poll_fn::<(), _>(|cx| self.poll_until_sync(cx))); + futures::executor::block_on(futures::future::poll_fn::<(), _>(|cx| { + self.poll_until_sync(cx) + })); } /// Blocks the current thread until there are no pending packets. /// /// Calls `poll_until_idle` repeatedly with the runtime passed as parameter. fn block_until_idle(&mut self) { - futures::executor::block_on(futures::future::poll_fn::<(), _>(|cx| self.poll_until_idle(cx))); + futures::executor::block_on(futures::future::poll_fn::<(), _>(|cx| { + self.poll_until_idle(cx) + })); } /// Blocks the current thread until all peers are connected to each other. /// /// Calls `poll_until_connected` repeatedly with the runtime passed as parameter. fn block_until_connected(&mut self) { - futures::executor::block_on( - futures::future::poll_fn::<(), _>(|cx| self.poll_until_connected(cx)), - ); + futures::executor::block_on(futures::future::poll_fn::<(), _>(|cx| { + self.poll_until_connected(cx) + })); } /// Polls the testnet. Processes all the pending actions. @@ -1029,13 +1052,17 @@ pub trait TestNetFactory: Sized where >: trace!(target: "sync", "-- Polling complete {}: {}", i, peer.id()); // We poll `imported_blocks_stream`. - while let Poll::Ready(Some(notification)) = peer.imported_blocks_stream.as_mut().poll_next(cx) { + while let Poll::Ready(Some(notification)) = + peer.imported_blocks_stream.as_mut().poll_next(cx) + { peer.network.service().announce_block(notification.hash, None); } // We poll `finality_notification_stream`, but we only take the last event. let mut last = None; - while let Poll::Ready(Some(item)) = peer.finality_notification_stream.as_mut().poll_next(cx) { + while let Poll::Ready(Some(item)) = + peer.finality_notification_stream.as_mut().poll_next(cx) + { last = Some(item); } if let Some(notification) = last { @@ -1054,10 +1081,7 @@ pub struct TestNet { impl TestNet { /// Create a `TestNet` that used the given fork choice rule. pub fn with_fork_choice(fork_choice: ForkChoiceStrategy) -> Self { - Self { - peers: Vec::new(), - fork_choice, - } + Self { peers: Vec::new(), fork_choice } } } @@ -1068,25 +1092,26 @@ impl TestNetFactory for TestNet { /// Create new test network with peers and given config. fn from_config(_config: &ProtocolConfig) -> Self { - TestNet { - peers: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - } + TestNet { peers: Vec::new(), fork_choice: ForkChoiceStrategy::LongestChain } } - fn make_verifier(&self, _client: PeersClient, _config: &ProtocolConfig, _peer_data: &()) - -> Self::Verifier - { + fn make_verifier( + &self, + _client: PeersClient, + _config: &ProtocolConfig, + _peer_data: &(), + ) -> Self::Verifier { PassThroughVerifier::new_with_fork_choice(false, self.fork_choice.clone()) } - fn make_block_import(&self, client: PeersClient) - -> ( - BlockImportAdapter, - Option>, - Self::PeerData, - ) - { + fn make_block_import( + &self, + client: PeersClient, + ) -> ( + BlockImportAdapter, + Option>, + Self::PeerData, + ) { (client.as_block_import(), None, ()) } @@ -1119,7 +1144,8 @@ impl JustificationImport for ForceFinalized { _number: NumberFor, justification: Justification, ) -> Result<(), Self::Error> { - self.0.finalize_block(BlockId::Hash(hash), Some(justification), true) + self.0 + .finalize_block(BlockId::Hash(hash), Some(justification), true) .map_err(|_| ConsensusError::InvalidJustification.into()) } } @@ -1135,7 +1161,12 @@ impl TestNetFactory for JustificationTestNet { JustificationTestNet(TestNet::from_config(config)) } - fn make_verifier(&self, client: PeersClient, config: &ProtocolConfig, peer_data: &()) -> Self::Verifier { + fn make_verifier( + &self, + client: PeersClient, + config: &ProtocolConfig, + peer_data: &(), + ) -> Self::Verifier { self.0.make_verifier(client, config, peer_data) } @@ -1147,23 +1178,21 @@ impl TestNetFactory for JustificationTestNet { self.0.peers() } - fn mut_peers>, - )>(&mut self, closure: F) { + fn mut_peers>)>( + &mut self, + closure: F, + ) { self.0.mut_peers(closure) } - fn make_block_import(&self, client: PeersClient) - -> ( - BlockImportAdapter, - Option>, - Self::PeerData, - ) - { - ( - client.as_block_import(), - Some(Box::new(ForceFinalized(client))), - Default::default(), - ) + fn make_block_import( + &self, + client: PeersClient, + ) -> ( + BlockImportAdapter, + Option>, + Self::PeerData, + ) { + (client.as_block_import(), Some(Box::new(ForceFinalized(client))), Default::default()) } } diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index cd09d878b2350..b815298f46286 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -16,26 +16,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::*; -use super::state_full::split_range; use self::error::Error; +use super::{state_full::split_range, *}; -use std::sync::Arc; +use crate::testing::TaskExecutor; use assert_matches::assert_matches; +use futures::{compat::Future01CompatExt, executor}; use futures01::stream::Stream; -use sp_core::{storage::ChildInfo, ChangesTrieConfiguration}; -use sp_core::hash::H256; use sc_block_builder::BlockBuilderProvider; -use sp_io::hashing::blake2_256; -use substrate_test_runtime_client::{ - prelude::*, - sp_consensus::BlockOrigin, - runtime, -}; use sc_rpc_api::DenyUnsafe; +use sp_core::{hash::H256, storage::ChildInfo, ChangesTrieConfiguration}; +use sp_io::hashing::blake2_256; use sp_runtime::generic::BlockId; -use crate::testing::TaskExecutor; -use futures::{executor, compat::Future01CompatExt}; +use std::sync::Arc; +use substrate_test_runtime_client::{prelude::*, runtime, sp_consensus::BlockOrigin}; const STORAGE_KEY: &[u8] = b"child"; @@ -68,12 +62,18 @@ fn should_return_storage() { let key = StorageKey(KEY.to_vec()); assert_eq!( - client.storage(key.clone(), Some(genesis_hash).into()).wait() - .map(|x| x.map(|x| x.0.len())).unwrap().unwrap() as usize, + client + .storage(key.clone(), Some(genesis_hash).into()) + .wait() + .map(|x| x.map(|x| x.0.len())) + .unwrap() + .unwrap() as usize, VALUE.len(), ); assert_matches!( - client.storage_hash(key.clone(), Some(genesis_hash).into()).wait() + client + .storage_hash(key.clone(), Some(genesis_hash).into()) + .wait() .map(|x| x.is_some()), Ok(true) ); @@ -87,10 +87,13 @@ fn should_return_storage() { ); assert_eq!( executor::block_on( - child.storage(prefixed_storage_key(), key, Some(genesis_hash).into()) + child + .storage(prefixed_storage_key(), key, Some(genesis_hash).into()) .map(|x| x.map(|x| x.0.len())) .compat(), - ).unwrap().unwrap() as usize, + ) + .unwrap() + .unwrap() as usize, CHILD_VALUE.len(), ); } @@ -98,20 +101,17 @@ fn should_return_storage() { #[test] fn should_return_child_storage() { let child_info = ChildInfo::new_default(STORAGE_KEY); - let client = Arc::new(substrate_test_runtime_client::TestClientBuilder::new() - .add_child_storage(&child_info, "key", vec![42_u8]) - .build()); - let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full( - client, - SubscriptionManager::new(Arc::new(TaskExecutor)), - DenyUnsafe::No, - None, + let client = Arc::new( + substrate_test_runtime_client::TestClientBuilder::new() + .add_child_storage(&child_info, "key", vec![42_u8]) + .build(), ); + let genesis_hash = client.genesis_hash(); + let (_client, child) = + new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None); let child_key = prefixed_storage_key(); let key = StorageKey(b"key".to_vec()); - assert_matches!( child.storage( child_key.clone(), @@ -121,36 +121,26 @@ fn should_return_child_storage() { Ok(Some(StorageData(ref d))) if d[0] == 42 && d.len() == 1 ); assert_matches!( - child.storage_hash( - child_key.clone(), - key.clone(), - Some(genesis_hash).into(), - ).wait().map(|x| x.is_some()), + child + .storage_hash(child_key.clone(), key.clone(), Some(genesis_hash).into(),) + .wait() + .map(|x| x.is_some()), Ok(true) ); - assert_matches!( - child.storage_size( - child_key.clone(), - key.clone(), - None, - ).wait(), - Ok(Some(1)) - ); + assert_matches!(child.storage_size(child_key.clone(), key.clone(), None,).wait(), Ok(Some(1))); } #[test] fn should_call_contract() { let client = Arc::new(substrate_test_runtime_client::new()); let genesis_hash = client.genesis_hash(); - let (client, _child) = new_full( - client, - SubscriptionManager::new(Arc::new(TaskExecutor)), - DenyUnsafe::No, - None, - ); + let (client, _child) = + new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None); assert_matches!( - client.call("balanceOf".into(), Bytes(vec![1,2,3]), Some(genesis_hash).into()).wait(), + client + .call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into()) + .wait(), Err(Error::Client(_)) ) } @@ -171,18 +161,17 @@ fn should_notify_about_storage_changes() { api.subscribe_storage(Default::default(), subscriber, None.into()); // assert id assigned - assert!(matches!( - executor::block_on(id.compat()), - Ok(Ok(SubscriptionId::String(_))) - )); + assert!(matches!(executor::block_on(id.compat()), Ok(Ok(SubscriptionId::String(_))))); let mut builder = client.new_block(Default::default()).unwrap(); - builder.push_transfer(runtime::Transfer { - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Ferdie.into(), - amount: 42, - nonce: 0, - }).unwrap(); + builder + .push_transfer(runtime::Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 42, + nonce: 0, + }) + .unwrap(); let block = builder.build().unwrap().block; executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); } @@ -207,25 +196,27 @@ fn should_send_initial_storage_changes_and_notifications() { None, ); - let alice_balance_key = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())); + let alice_balance_key = + blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())); - api.subscribe_storage(Default::default(), subscriber, Some(vec![ - StorageKey(alice_balance_key.to_vec()), - ]).into()); + api.subscribe_storage( + Default::default(), + subscriber, + Some(vec![StorageKey(alice_balance_key.to_vec())]).into(), + ); // assert id assigned - assert!(matches!( - executor::block_on(id.compat()), - Ok(Ok(SubscriptionId::String(_))) - )); + assert!(matches!(executor::block_on(id.compat()), Ok(Ok(SubscriptionId::String(_))))); let mut builder = client.new_block(Default::default()).unwrap(); - builder.push_transfer(runtime::Transfer { - from: AccountKeyring::Alice.into(), - to: AccountKeyring::Ferdie.into(), - amount: 42, - nonce: 0, - }).unwrap(); + builder + .push_transfer(runtime::Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 42, + nonce: 0, + }) + .unwrap(); let block = builder.build().unwrap().block; executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); } @@ -257,9 +248,13 @@ fn should_query_storage() { // fake change: None -> Some(value) -> Some(value) builder.push_storage_change(vec![2], Some(vec![2])).unwrap(); // actual change: None -> Some(value) -> None - builder.push_storage_change(vec![3], if nonce == 0 { Some(vec![3]) } else { None }).unwrap(); + builder + .push_storage_change(vec![3], if nonce == 0 { Some(vec![3]) } else { None }) + .unwrap(); // actual change: None -> Some(value) - builder.push_storage_change(vec![4], if nonce == 0 { None } else { Some(vec![4]) }).unwrap(); + builder + .push_storage_change(vec![4], if nonce == 0 { None } else { Some(vec![4]) }) + .unwrap(); // actual change: Some(value1) -> Some(value2) builder.push_storage_change(vec![5], Some(vec![nonce as u8])).unwrap(); let block = builder.build().unwrap().block; @@ -301,20 +296,12 @@ fn should_query_storage() { // Query changes only up to block1 let keys = (1..6).map(|k| StorageKey(vec![k])).collect::>(); - let result = api.query_storage( - keys.clone(), - genesis_hash, - Some(block1_hash).into(), - ); + let result = api.query_storage(keys.clone(), genesis_hash, Some(block1_hash).into()); assert_eq!(result.wait().unwrap(), expected); // Query all changes - let result = api.query_storage( - keys.clone(), - genesis_hash, - None.into(), - ); + let result = api.query_storage(keys.clone(), genesis_hash, None.into()); expected.push(StorageChangeSet { block: block2_hash, @@ -327,20 +314,12 @@ fn should_query_storage() { assert_eq!(result.wait().unwrap(), expected); // Query changes up to block2. - let result = api.query_storage( - keys.clone(), - genesis_hash, - Some(block2_hash), - ); + let result = api.query_storage(keys.clone(), genesis_hash, Some(block2_hash)); assert_eq!(result.wait().unwrap(), expected); // Inverted range. - let result = api.query_storage( - keys.clone(), - block1_hash, - Some(genesis_hash), - ); + let result = api.query_storage(keys.clone(), block1_hash, Some(genesis_hash)); assert_eq!( result.wait().map_err(|e| e.to_string()), @@ -348,96 +327,92 @@ fn should_query_storage() { from: format!("1 ({:?})", block1_hash), to: format!("0 ({:?})", genesis_hash), details: "from number > to number".to_owned(), - }).map_err(|e| e.to_string()) + }) + .map_err(|e| e.to_string()) ); let random_hash1 = H256::random(); let random_hash2 = H256::random(); // Invalid second hash. - let result = api.query_storage( - keys.clone(), - genesis_hash, - Some(random_hash1), - ); + let result = api.query_storage(keys.clone(), genesis_hash, Some(random_hash1)); assert_eq!( result.wait().map_err(|e| e.to_string()), Err(Error::InvalidBlockRange { from: format!("{:?}", genesis_hash), to: format!("{:?}", Some(random_hash1)), - details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), - }).map_err(|e| e.to_string()) + details: format!( + "UnknownBlock: Header was not found in the database: {:?}", + random_hash1 + ), + }) + .map_err(|e| e.to_string()) ); // Invalid first hash with Some other hash. - let result = api.query_storage( - keys.clone(), - random_hash1, - Some(genesis_hash), - ); + let result = api.query_storage(keys.clone(), random_hash1, Some(genesis_hash)); assert_eq!( result.wait().map_err(|e| e.to_string()), Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), to: format!("{:?}", Some(genesis_hash)), - details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), - }).map_err(|e| e.to_string()), + details: format!( + "UnknownBlock: Header was not found in the database: {:?}", + random_hash1 + ), + }) + .map_err(|e| e.to_string()), ); // Invalid first hash with None. - let result = api.query_storage( - keys.clone(), - random_hash1, - None, - ); + let result = api.query_storage(keys.clone(), random_hash1, None); assert_eq!( result.wait().map_err(|e| e.to_string()), Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), to: format!("{:?}", Some(block2_hash)), // Best block hash. - details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), - }).map_err(|e| e.to_string()), + details: format!( + "UnknownBlock: Header was not found in the database: {:?}", + random_hash1 + ), + }) + .map_err(|e| e.to_string()), ); // Both hashes invalid. - let result = api.query_storage( - keys.clone(), - random_hash1, - Some(random_hash2), - ); + let result = api.query_storage(keys.clone(), random_hash1, Some(random_hash2)); assert_eq!( result.wait().map_err(|e| e.to_string()), Err(Error::InvalidBlockRange { from: format!("{:?}", random_hash1), // First hash not found. to: format!("{:?}", Some(random_hash2)), - details: format!("UnknownBlock: Header was not found in the database: {:?}", random_hash1), - }).map_err(|e| e.to_string()), + details: format!( + "UnknownBlock: Header was not found in the database: {:?}", + random_hash1 + ), + }) + .map_err(|e| e.to_string()), ); // single block range - let result = api.query_storage_at( - keys.clone(), - Some(block1_hash), - ); + let result = api.query_storage_at(keys.clone(), Some(block1_hash)); assert_eq!( result.wait().unwrap(), - vec![ - StorageChangeSet { - block: block1_hash, - changes: vec![ - (StorageKey(vec![1_u8]), None), - (StorageKey(vec![2_u8]), Some(StorageData(vec![2_u8]))), - (StorageKey(vec![3_u8]), Some(StorageData(vec![3_u8]))), - (StorageKey(vec![4_u8]), None), - (StorageKey(vec![5_u8]), Some(StorageData(vec![0_u8]))), - ] - } - ] + vec![StorageChangeSet { + block: block1_hash, + changes: vec![ + (StorageKey(vec![1_u8]), None), + (StorageKey(vec![2_u8]), Some(StorageData(vec![2_u8]))), + (StorageKey(vec![3_u8]), Some(StorageData(vec![3_u8]))), + (StorageKey(vec![4_u8]), None), + (StorageKey(vec![5_u8]), Some(StorageData(vec![0_u8]))), + ] + }] ); } @@ -461,7 +436,6 @@ fn should_split_ranges() { assert_eq!(split_range(100, Some(99)), (0..99, Some(99..100))); } - #[test] fn should_return_runtime_version() { let client = Arc::new(substrate_test_runtime_client::new()); @@ -503,17 +477,13 @@ fn should_notify_on_runtime_version_initially() { api.subscribe_runtime_version(Default::default(), subscriber); // assert id assigned - assert!(matches!( - executor::block_on(id.compat()), - Ok(Ok(SubscriptionId::String(_))) - )); - + assert!(matches!(executor::block_on(id.compat()), Ok(Ok(SubscriptionId::String(_))))); } // assert initial version sent. let (notification, next) = executor::block_on(transport.into_future().compat()).unwrap(); assert!(notification.is_some()); - // no more notifications on this channel + // no more notifications on this channel assert_eq!(executor::block_on(next.into_future().compat()).unwrap().0, None); } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 62368259f372b..603e24ad71997 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -17,66 +17,53 @@ // along with this program. If not, see . use crate::{ - error::Error, MallocSizeOfWasm, RpcHandlers, - start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle, - metrics::MetricsService, + build_network_future, client::{light, Client, ClientConfig}, config::{Configuration, KeystoreConfig, PrometheusConfig, TransactionStorageMode}, + error::Error, + metrics::MetricsService, + start_rpc_servers, MallocSizeOfWasm, RpcHandlers, SpawnTaskHandle, TaskManager, + TransactionPoolAdapter, }; -use sc_client_api::{ - light::RemoteBlockchain, ForkBlocks, BadBlocks, UsageProvider, ExecutorProvider, -}; -use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; -use sc_chain_spec::get_extension; -use sp_consensus::{ - block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator, Chain}, - import_queue::ImportQueue, -}; +use futures::{channel::oneshot, future::ready, FutureExt, StreamExt}; use jsonrpc_pubsub::manager::SubscriptionManager; -use futures::{ - FutureExt, StreamExt, - future::ready, - channel::oneshot, -}; -use sc_keystore::LocalKeystore; use log::info; -use sc_network::config::{Role, OnDemand, SyncMode}; -use sc_network::NetworkService; -use sc_network::block_request_handler::{self, BlockRequestHandler}; -use sc_network::state_request_handler::{self, StateRequestHandler}; -use sc_network::warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider}; -use sc_network::light_client_requests::{self, handler::LightClientRequestHandler}; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::{ - Block as BlockT, HashFor, Zero, BlockIdTo, +use prometheus_endpoint::Registry; +use sc_chain_spec::get_extension; +use sc_client_api::{ + execution_extensions::ExecutionExtensions, light::RemoteBlockchain, + proof_provider::ProofProvider, BadBlocks, BlockBackend, BlockchainEvents, ExecutorProvider, + ForkBlocks, StorageProvider, UsageProvider, }; -use sp_api::{ProvideRuntimeApi, CallApiAt}; -use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo}; -use std::{sync::Arc, str::FromStr}; -use wasm_timer::SystemTime; -use sc_telemetry::{ - telemetry, - ConnectionMessage, - Telemetry, - TelemetryHandle, - SUBSTRATE_INFO, +use sc_client_db::{Backend, DatabaseSettings}; +use sc_executor::{NativeExecutionDispatch, NativeExecutor, RuntimeInfo}; +use sc_keystore::LocalKeystore; +use sc_network::{ + block_request_handler::{self, BlockRequestHandler}, + config::{OnDemand, Role, SyncMode}, + light_client_requests::{self, handler::LightClientRequestHandler}, + state_request_handler::{self, StateRequestHandler}, + warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider}, + NetworkService, }; +use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sc_transaction_pool_api::MaintainedTransactionPool; -use prometheus_endpoint::Registry; -use sc_client_db::{Backend, DatabaseSettings}; -use sp_core::traits::{ - CodeExecutor, - SpawnNamed, +use sp_api::{CallApiAt, ProvideRuntimeApi}; +use sp_blockchain::{HeaderBackend, HeaderMetadata}; +use sp_consensus::{ + block_validation::{BlockAnnounceValidator, Chain, DefaultBlockAnnounceValidator}, + import_queue::ImportQueue, }; +use sp_core::traits::{CodeExecutor, SpawnNamed}; use sp_keystore::{CryptoStore, SyncCryptoStore, SyncCryptoStorePtr}; -use sp_runtime::BuildStorage; -use sc_client_api::{ - BlockBackend, BlockchainEvents, - StorageProvider, - proof_provider::ProofProvider, - execution_extensions::ExecutionExtensions +use sp_runtime::{ + generic::BlockId, + traits::{Block as BlockT, BlockIdTo, HashFor, Zero}, + BuildStorage, }; -use sp_blockchain::{HeaderMetadata, HeaderBackend}; +use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; +use std::{str::FromStr, sync::Arc}; +use wasm_timer::SystemTime; /// A utility trait for building an RPC extension given a `DenyUnsafe` instance. /// This is useful since at service definition time we don't know whether the @@ -96,7 +83,8 @@ pub trait RpcExtensionBuilder { ) -> Self::Output; } -impl RpcExtensionBuilder for F where +impl RpcExtensionBuilder for F +where F: Fn(sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> R, R: sc_rpc::RpcExtension, { @@ -116,7 +104,8 @@ impl RpcExtensionBuilder for F where /// `DenyUnsafe` instance and return a static `RpcExtension` instance. pub struct NoopRpcExtensionBuilder(pub R); -impl RpcExtensionBuilder for NoopRpcExtensionBuilder where +impl RpcExtensionBuilder for NoopRpcExtensionBuilder +where R: Clone + sc_rpc::RpcExtension, { type Output = R; @@ -130,7 +119,8 @@ impl RpcExtensionBuilder for NoopRpcExtensionBuilder where } } -impl From for NoopRpcExtensionBuilder where +impl From for NoopRpcExtensionBuilder +where R: sc_rpc::RpcExtension, { fn from(e: R) -> NoopRpcExtensionBuilder { @@ -138,58 +128,37 @@ impl From for NoopRpcExtensionBuilder where } } - /// Full client type. -pub type TFullClient = Client< - TFullBackend, - TFullCallExecutor, - TBl, - TRtApi, ->; +pub type TFullClient = + Client, TFullCallExecutor, TBl, TRtApi>; /// Full client backend type. pub type TFullBackend = sc_client_db::Backend; /// Full client call executor type. -pub type TFullCallExecutor = crate::client::LocalCallExecutor< - TBl, - sc_client_db::Backend, - NativeExecutor, ->; +pub type TFullCallExecutor = + crate::client::LocalCallExecutor, NativeExecutor>; /// Light client type. -pub type TLightClient = TLightClientWithBackend< - TBl, TRtApi, TExecDisp, TLightBackend ->; +pub type TLightClient = + TLightClientWithBackend>; /// Light client backend type. -pub type TLightBackend = sc_light::Backend< - sc_client_db::light::LightStorage, - HashFor, ->; +pub type TLightBackend = + sc_light::Backend, HashFor>; /// Light call executor type. pub type TLightCallExecutor = sc_light::GenesisCallExecutor< - sc_light::Backend< - sc_client_db::light::LightStorage, - HashFor - >, + sc_light::Backend, HashFor>, crate::client::LocalCallExecutor< TBl, - sc_light::Backend< - sc_client_db::light::LightStorage, - HashFor - >, - NativeExecutor + sc_light::Backend, HashFor>, + NativeExecutor, >, >; -type TFullParts = ( - TFullClient, - Arc>, - KeystoreContainer, - TaskManager, -); +type TFullParts = + (TFullClient, Arc>, KeystoreContainer, TaskManager); type TLightParts = ( Arc>, @@ -200,10 +169,8 @@ type TLightParts = ( ); /// Light client backend type with a specific hash type. -pub type TLightBackendWithHash = sc_light::Backend< - sc_client_db::light::LightStorage, - THash, ->; +pub type TLightBackendWithHash = + sc_light::Backend, THash>; /// Light client type with a specific backend. pub type TLightClientWithBackend = Client< @@ -221,7 +188,10 @@ trait AsCryptoStoreRef { fn sync_keystore_ref(&self) -> Arc; } -impl AsCryptoStoreRef for Arc where T: CryptoStore + SyncCryptoStore + 'static { +impl AsCryptoStoreRef for Arc +where + T: CryptoStore + SyncCryptoStore + 'static, +{ fn keystore_ref(&self) -> Arc { self.clone() } @@ -240,14 +210,12 @@ impl KeystoreContainer { /// Construct KeystoreContainer pub fn new(config: &KeystoreConfig) -> Result { let keystore = Arc::new(match config { - KeystoreConfig::Path { path, password } => LocalKeystore::open( - path.clone(), - password.clone(), - )?, + KeystoreConfig::Path { path, password } => + LocalKeystore::open(path.clone(), password.clone())?, KeystoreConfig::InMemory => LocalKeystore::in_memory(), }); - Ok(Self{remote: Default::default(), local: keystore}) + Ok(Self { remote: Default::default(), local: keystore }) } /// Set the remote keystore. @@ -256,7 +224,8 @@ impl KeystoreContainer { /// does not reset any references previously handed out - they will /// stick around. pub fn set_remote_keystore(&mut self, remote: Arc) - where T: CryptoStore + SyncCryptoStore + 'static + where + T: CryptoStore + SyncCryptoStore + 'static, { self.remote = Some(Box::new(remote)) } @@ -296,7 +265,8 @@ impl KeystoreContainer { pub fn new_full_client( config: &Configuration, telemetry: Option, -) -> Result, Error> where +) -> Result, Error> +where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, TBl::Hash: FromStr, @@ -308,7 +278,8 @@ pub fn new_full_client( pub fn new_full_parts( config: &Configuration, telemetry: Option, -) -> Result, Error> where +) -> Result, Error> +where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, TBl::Hash: FromStr, @@ -338,15 +309,13 @@ pub fn new_full_parts( let (client, backend) = { let db_config = sc_client_db::DatabaseSettings { state_cache_size: config.state_cache_size, - state_cache_child_ratio: - config.state_cache_child_ratio.map(|v| (v, 100)), + state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), state_pruning: config.state_pruning.clone(), source: config.database.clone(), keep_blocks: config.keep_blocks.clone(), transaction_storage: config.transaction_storage.clone(), }; - let backend = new_db_backend(db_config)?; let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new( @@ -355,15 +324,20 @@ pub fn new_full_parts( sc_offchain::OffchainDb::factory_from_backend(&*backend), ); - let wasm_runtime_substitutes = config.chain_spec.code_substitutes().into_iter().map(|(h, c)| { - let hash = TBl::Hash::from_str(&h) - .map_err(|_| - Error::Application(Box::from( - format!("Failed to parse `{}` as block hash for code substitutes.", h) - )) - )?; - Ok((hash, c)) - }).collect::, Error>>()?; + let wasm_runtime_substitutes = config + .chain_spec + .code_substitutes() + .into_iter() + .map(|(h, c)| { + let hash = TBl::Hash::from_str(&h).map_err(|_| { + Error::Application(Box::from(format!( + "Failed to parse `{}` as block hash for code substitutes.", + h + ))) + })?; + Ok((hash, c)) + }) + .collect::, Error>>()?; let client = new_client( backend.clone(), @@ -376,11 +350,16 @@ pub fn new_full_parts( config.prometheus_config.as_ref().map(|config| config.registry.clone()), telemetry, ClientConfig { - offchain_worker_enabled : config.offchain_worker.enabled, + offchain_worker_enabled: config.offchain_worker.enabled, offchain_indexing_api: config.offchain_worker.indexing_enabled, wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), - no_genesis: matches!(config.network.sync_mode, sc_network::config::SyncMode::Fast {..}) - || matches!(config.network.sync_mode, sc_network::config::SyncMode::Warp), + no_genesis: matches!( + config.network.sync_mode, + sc_network::config::SyncMode::Fast { .. } + ) || matches!( + config.network.sync_mode, + sc_network::config::SyncMode::Warp + ), wasm_runtime_substitutes, }, )?; @@ -388,19 +367,15 @@ pub fn new_full_parts( (client, backend) }; - Ok(( - client, - backend, - keystore_container, - task_manager, - )) + Ok((client, backend, keystore_container, task_manager)) } /// Create the initial parts of a light node. pub fn new_light_parts( config: &Configuration, telemetry: Option, -) -> Result, Error> where +) -> Result, Error> +where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, { @@ -419,8 +394,7 @@ pub fn new_light_parts( let db_storage = { let db_settings = sc_client_db::DatabaseSettings { state_cache_size: config.state_cache_size, - state_cache_child_ratio: - config.state_cache_child_ratio.map(|v| (v, 100)), + state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), state_pruning: config.state_pruning.clone(), source: config.database.clone(), keep_blocks: config.keep_blocks.clone(), @@ -429,13 +403,11 @@ pub fn new_light_parts( sc_client_db::light::LightStorage::new(db_settings)? }; let light_blockchain = sc_light::new_light_blockchain(db_storage); - let fetch_checker = Arc::new( - sc_light::new_fetch_checker::<_, TBl, _>( - light_blockchain.clone(), - executor.clone(), - Box::new(task_manager.spawn_handle()), - ), - ); + let fetch_checker = Arc::new(sc_light::new_fetch_checker::<_, TBl, _>( + light_blockchain.clone(), + executor.clone(), + Box::new(task_manager.spawn_handle()), + )); let on_demand = Arc::new(sc_network::config::OnDemand::new(fetch_checker)); let backend = sc_light::new_light_backend(light_blockchain); let client = Arc::new(light::new_light( @@ -453,7 +425,8 @@ pub fn new_light_parts( /// Create an instance of default DB-backend backend. pub fn new_db_backend( settings: DatabaseSettings, -) -> Result>, sp_blockchain::Error> where +) -> Result>, sp_blockchain::Error> +where Block: BlockT, { const CANONICALIZATION_DELAY: u64 = 4096; @@ -482,11 +455,16 @@ pub fn new_client( >, sp_blockchain::Error, > - where - Block: BlockT, - E: CodeExecutor + RuntimeInfo, +where + Block: BlockT, + E: CodeExecutor + RuntimeInfo, { - let executor = crate::client::LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?; + let executor = crate::client::LocalCallExecutor::new( + backend.clone(), + executor, + spawn_handle, + config.clone(), + )?; Ok(crate::client::Client::new( backend, executor, @@ -536,10 +514,10 @@ pub fn build_offchain_workers( client: Arc, network: Arc::Hash>>, ) -> Option>> - where - TBl: BlockT, - TCl: Send + Sync + ProvideRuntimeApi + BlockchainEvents + 'static, - >::Api: sc_offchain::OffchainWorkerApi, +where + TBl: BlockT, + TCl: Send + Sync + ProvideRuntimeApi + BlockchainEvents + 'static, + >::Api: sc_offchain::OffchainWorkerApi, { let offchain_workers = Some(Arc::new(sc_offchain::OffchainWorkers::new(client.clone()))); @@ -553,7 +531,7 @@ pub fn build_offchain_workers( offchain, Clone::clone(&spawn_handle), network.clone(), - ) + ), ); } @@ -564,22 +542,32 @@ pub fn build_offchain_workers( pub fn spawn_tasks( params: SpawnTasksParams, ) -> Result - where - TCl: ProvideRuntimeApi + HeaderMetadata + Chain + - BlockBackend + BlockIdTo + ProofProvider + - HeaderBackend + BlockchainEvents + ExecutorProvider + UsageProvider + - StorageProvider + CallApiAt + Send + 'static, - >::Api: - sp_api::Metadata + - sc_offchain::OffchainWorkerApi + - sp_transaction_pool::runtime_api::TaggedTransactionQueue + - sp_session::SessionKeys + - sp_api::ApiExt, - TBl: BlockT, - TBackend: 'static + sc_client_api::backend::Backend + Send, - TExPool: MaintainedTransactionPool::Hash> + - MallocSizeOfWasm + 'static, - TRpc: sc_rpc::RpcExtension +where + TCl: ProvideRuntimeApi + + HeaderMetadata + + Chain + + BlockBackend + + BlockIdTo + + ProofProvider + + HeaderBackend + + BlockchainEvents + + ExecutorProvider + + UsageProvider + + StorageProvider + + CallApiAt + + Send + + 'static, + >::Api: sp_api::Metadata + + sc_offchain::OffchainWorkerApi + + sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_session::SessionKeys + + sp_api::ApiExt, + TBl: BlockT, + TBackend: 'static + sc_client_api::backend::Backend + Send, + TExPool: MaintainedTransactionPool::Hash> + + MallocSizeOfWasm + + 'static, + TRpc: sc_rpc::RpcExtension, { let SpawnTasksParams { mut config, @@ -602,17 +590,11 @@ pub fn spawn_tasks( client.clone(), &BlockId::Hash(chain_info.best_hash), config.dev_key_seed.clone().map(|s| vec![s]).unwrap_or_default(), - ).map_err(|e| Error::Application(Box::new(e)))?; + ) + .map_err(|e| Error::Application(Box::new(e)))?; let telemetry = telemetry - .map(|telemetry| { - init_telemetry( - &mut config, - network.clone(), - client.clone(), - telemetry, - ) - }) + .map(|telemetry| init_telemetry(&mut config, network.clone(), client.clone(), telemetry)) .transpose()?; info!("📦 Highest known block at #{}", chain_info.best_number); @@ -627,63 +609,69 @@ pub fn spawn_tasks( spawn_handle.spawn( "on-transaction-imported", - transaction_notifications( - transaction_pool.clone(), - network.clone(), - telemetry.clone(), - ), + transaction_notifications(transaction_pool.clone(), network.clone(), telemetry.clone()), ); // Prometheus metrics. - let metrics_service = if let Some(PrometheusConfig { port, registry }) = - config.prometheus_config.clone() - { - // Set static metrics. - let metrics = MetricsService::with_prometheus(telemetry.clone(), ®istry, &config)?; - spawn_handle.spawn( - "prometheus-endpoint", - prometheus_endpoint::init_prometheus(port, registry).map(drop) - ); + let metrics_service = + if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() { + // Set static metrics. + let metrics = MetricsService::with_prometheus(telemetry.clone(), ®istry, &config)?; + spawn_handle.spawn( + "prometheus-endpoint", + prometheus_endpoint::init_prometheus(port, registry).map(drop), + ); - metrics - } else { - MetricsService::new(telemetry.clone()) - }; + metrics + } else { + MetricsService::new(telemetry.clone()) + }; // Periodically updated metrics and telemetry updates. - spawn_handle.spawn("telemetry-periodic-send", - metrics_service.run( - client.clone(), - transaction_pool.clone(), - network.clone(), - ) + spawn_handle.spawn( + "telemetry-periodic-send", + metrics_service.run(client.clone(), transaction_pool.clone(), network.clone()), ); // RPC - let gen_handler = | - deny_unsafe: sc_rpc::DenyUnsafe, - rpc_middleware: sc_rpc_server::RpcMiddleware - | gen_handler( - deny_unsafe, rpc_middleware, &config, task_manager.spawn_handle(), - client.clone(), transaction_pool.clone(), keystore.clone(), - on_demand.clone(), remote_blockchain.clone(), &*rpc_extensions_builder, - backend.offchain_storage(), system_rpc_tx.clone() - ); + let gen_handler = |deny_unsafe: sc_rpc::DenyUnsafe, + rpc_middleware: sc_rpc_server::RpcMiddleware| { + gen_handler( + deny_unsafe, + rpc_middleware, + &config, + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + keystore.clone(), + on_demand.clone(), + remote_blockchain.clone(), + &*rpc_extensions_builder, + backend.offchain_storage(), + system_rpc_tx.clone(), + ) + }; let rpc_metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; let rpc = start_rpc_servers(&config, gen_handler, rpc_metrics.clone())?; // This is used internally, so don't restrict access to unsafe RPC - let rpc_handlers = RpcHandlers(Arc::new(gen_handler( - sc_rpc::DenyUnsafe::No, - sc_rpc_server::RpcMiddleware::new(rpc_metrics, "inbrowser") - ).into())); + let rpc_handlers = RpcHandlers(Arc::new( + gen_handler( + sc_rpc::DenyUnsafe::No, + sc_rpc_server::RpcMiddleware::new(rpc_metrics, "inbrowser"), + ) + .into(), + )); // Spawn informant task - spawn_handle.spawn("informant", sc_informant::build( - client.clone(), - network.clone(), - transaction_pool.clone(), - config.informant_output_format, - )); + spawn_handle.spawn( + "informant", + sc_informant::build( + client.clone(), + network.clone(), + transaction_pool.clone(), + config.informant_output_format, + ), + ); task_manager.keep_alive((config.base_path, rpc, rpc_handlers.clone())); @@ -694,10 +682,9 @@ async fn transaction_notifications( transaction_pool: Arc, network: Arc::Hash>>, telemetry: Option, -) - where - TBl: BlockT, - TExPool: MaintainedTransactionPool::Hash>, +) where + TBl: BlockT, + TExPool: MaintainedTransactionPool::Hash>, { // transaction notifications transaction_pool @@ -732,9 +719,11 @@ fn init_telemetry>( chain: config.chain_spec.name().to_owned(), genesis_hash: format!("{:?}", genesis_hash), authority: config.role.is_authority(), - startup_time: SystemTime::UNIX_EPOCH.elapsed() + startup_time: SystemTime::UNIX_EPOCH + .elapsed() .map(|dur| dur.as_millis()) - .unwrap_or(0).to_string(), + .unwrap_or(0) + .to_string(), network_id: network.local_peer_id().to_base58(), }; @@ -755,22 +744,28 @@ fn gen_handler( remote_blockchain: Option>>, rpc_extensions_builder: &(dyn RpcExtensionBuilder + Send), offchain_storage: Option<>::OffchainStorage>, - system_rpc_tx: TracingUnboundedSender> + system_rpc_tx: TracingUnboundedSender>, ) -> sc_rpc_server::RpcHandler - where - TBl: BlockT, - TCl: ProvideRuntimeApi + BlockchainEvents + HeaderBackend + - HeaderMetadata + ExecutorProvider + - CallApiAt + ProofProvider + - StorageProvider + BlockBackend + Send + Sync + 'static, - TExPool: MaintainedTransactionPool::Hash> + 'static, - TBackend: sc_client_api::backend::Backend + 'static, - TRpc: sc_rpc::RpcExtension, - >::Api: - sp_session::SessionKeys + - sp_api::Metadata, +where + TBl: BlockT, + TCl: ProvideRuntimeApi + + BlockchainEvents + + HeaderBackend + + HeaderMetadata + + ExecutorProvider + + CallApiAt + + ProofProvider + + StorageProvider + + BlockBackend + + Send + + Sync + + 'static, + TExPool: MaintainedTransactionPool::Hash> + 'static, + TBackend: sc_client_api::backend::Backend + 'static, + TRpc: sc_rpc::RpcExtension, + >::Api: sp_session::SessionKeys + sp_api::Metadata, { - use sc_rpc::{chain, state, author, system, offchain}; + use sc_rpc::{author, chain, offchain, state, system}; let system_info = sc_rpc::system::SystemInfo { chain_name: config.chain_spec.name().into(), @@ -783,43 +778,37 @@ fn gen_handler( let task_executor = sc_rpc::SubscriptionTaskExecutor::new(spawn_handle); let subscriptions = SubscriptionManager::new(Arc::new(task_executor.clone())); - let (chain, state, child_state) = if let (Some(remote_blockchain), Some(on_demand)) = - (remote_blockchain, on_demand) { - // Light clients - let chain = sc_rpc::chain::new_light( - client.clone(), - subscriptions.clone(), - remote_blockchain.clone(), - on_demand.clone(), - ); - let (state, child_state) = sc_rpc::state::new_light( - client.clone(), - subscriptions.clone(), - remote_blockchain.clone(), - on_demand, - deny_unsafe, - ); - (chain, state, child_state) - - } else { - // Full nodes - let chain = sc_rpc::chain::new_full(client.clone(), subscriptions.clone()); - let (state, child_state) = sc_rpc::state::new_full( - client.clone(), - subscriptions.clone(), - deny_unsafe, - config.rpc_max_payload, - ); - (chain, state, child_state) - }; + let (chain, state, child_state) = + if let (Some(remote_blockchain), Some(on_demand)) = (remote_blockchain, on_demand) { + // Light clients + let chain = sc_rpc::chain::new_light( + client.clone(), + subscriptions.clone(), + remote_blockchain.clone(), + on_demand.clone(), + ); + let (state, child_state) = sc_rpc::state::new_light( + client.clone(), + subscriptions.clone(), + remote_blockchain.clone(), + on_demand, + deny_unsafe, + ); + (chain, state, child_state) + } else { + // Full nodes + let chain = sc_rpc::chain::new_full(client.clone(), subscriptions.clone()); + let (state, child_state) = sc_rpc::state::new_full( + client.clone(), + subscriptions.clone(), + deny_unsafe, + config.rpc_max_payload, + ); + (chain, state, child_state) + }; - let author = sc_rpc::author::Author::new( - client, - transaction_pool, - subscriptions, - keystore, - deny_unsafe, - ); + let author = + sc_rpc::author::Author::new(client, transaction_pool, subscriptions, keystore, deny_unsafe); let system = system::System::new(system_info, system_rpc_tx, deny_unsafe); let maybe_offchain_rpc = offchain_storage.map(|storage| { @@ -837,7 +826,7 @@ fn gen_handler( system::SystemApi::to_delegate(system), rpc_extensions_builder.build(deny_unsafe, task_executor), ), - rpc_middleware + rpc_middleware, ) } @@ -856,35 +845,46 @@ pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> { /// An optional, shared data fetcher for light clients. pub on_demand: Option>>, /// A block announce validator builder. - pub block_announce_validator_builder: Option) -> Box + Send> + Send - >>, + pub block_announce_validator_builder: + Option) -> Box + Send> + Send>>, /// An optional warp sync provider. pub warp_sync: Option>>, } /// Build the network service, the network status sinks and an RPC sender. pub fn build_network( - params: BuildNetworkParams + params: BuildNetworkParams, ) -> Result< ( Arc::Hash>>, TracingUnboundedSender>, NetworkStarter, ), - Error + Error, > - where - TBl: BlockT, - TCl: ProvideRuntimeApi + HeaderMetadata + Chain + - BlockBackend + BlockIdTo + ProofProvider + - HeaderBackend + BlockchainEvents + 'static, - TExPool: MaintainedTransactionPool::Hash> + 'static, - TImpQu: ImportQueue + 'static, +where + TBl: BlockT, + TCl: ProvideRuntimeApi + + HeaderMetadata + + Chain + + BlockBackend + + BlockIdTo + + ProofProvider + + HeaderBackend + + BlockchainEvents + + 'static, + TExPool: MaintainedTransactionPool::Hash> + 'static, + TImpQu: ImportQueue + 'static, { let BuildNetworkParams { - config, client, transaction_pool, spawn_handle, import_queue, on_demand, - block_announce_validator_builder, warp_sync, + config, + client, + transaction_pool, + spawn_handle, + import_queue, + on_demand, + block_announce_validator_builder, + warp_sync, } = params; let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { @@ -910,8 +910,8 @@ pub fn build_network( let (handler, protocol_config) = BlockRequestHandler::new( &protocol_id, client.clone(), - config.network.default_peers_set.in_peers as usize - + config.network.default_peers_set.out_peers as usize, + config.network.default_peers_set.in_peers as usize + + config.network.default_peers_set.out_peers as usize, ); spawn_handle.spawn("block_request_handler", handler.run()); protocol_config @@ -927,8 +927,8 @@ pub fn build_network( let (handler, protocol_config) = StateRequestHandler::new( &protocol_id, client.clone(), - config.network.default_peers_set.in_peers as usize - + config.network.default_peers_set.out_peers as usize, + config.network.default_peers_set.in_peers as usize + + config.network.default_peers_set.out_peers as usize, ); spawn_handle.spawn("state_request_handler", handler.run()); protocol_config @@ -941,10 +941,8 @@ pub fn build_network( warp_request_handler::generate_request_response_config(protocol_id.clone()) } else { // Allow both outgoing and incoming requests. - let (handler, protocol_config) = WarpSyncRequestHandler::new( - protocol_id.clone(), - provider.clone(), - ); + let (handler, protocol_config) = + WarpSyncRequestHandler::new(protocol_id.clone(), provider.clone()); spawn_handle.spawn("warp_sync_request_handler", handler.run()); protocol_config }; @@ -957,10 +955,8 @@ pub fn build_network( light_client_requests::generate_protocol_config(&protocol_id) } else { // Allow both outgoing and incoming requests. - let (handler, protocol_config) = LightClientRequestHandler::new( - &protocol_id, - client.clone(), - ); + let (handler, protocol_config) = + LightClientRequestHandler::new(&protocol_id, client.clone()); spawn_handle.spawn("light_client_request_handler", handler.run()); protocol_config } @@ -982,7 +978,7 @@ pub fn build_network( }, network_config: config.network.clone(), chain: client.clone(), - on_demand: on_demand, + on_demand, transaction_pool: transaction_pool_adapter as _, import_queue: Box::new(import_queue), protocol_id, @@ -997,10 +993,8 @@ pub fn build_network( // Storage chains don't keep full block history and can't be synced in full mode. // Force fast sync when storage chain mode is enabled. if matches!(config.transaction_storage, TransactionStorageMode::StorageChain) { - network_params.network_config.sync_mode = SyncMode::Fast { - storage_chain_mode: true, - skip_proofs: false, - }; + network_params.network_config.sync_mode = + SyncMode::Fast { storage_chain_mode: true, skip_proofs: false }; } let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); @@ -1049,7 +1043,7 @@ pub fn build_network( ); // This `return` might seem unnecessary, but we don't want to make it look like // everything is working as normal even though the user is clearly misusing the API. - return; + return } future.await diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 5cbfc1aa448e0..e7927904765df 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -18,98 +18,94 @@ //! Substrate Client -use std::{ - marker::PhantomData, - collections::{HashSet, BTreeMap, HashMap}, - sync::Arc, panic::UnwindSafe, result, - path::PathBuf +use super::{ + block_rules::{BlockRules, LookupResult as BlockLookupResult}, + genesis, }; +use codec::{Decode, Encode}; +use hash_db::Prefix; use log::{info, trace, warn}; use parking_lot::{Mutex, RwLock}; -use codec::{Encode, Decode}; -use hash_db::Prefix; +use prometheus_endpoint::Registry; +use rand::Rng; +use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof}; +use sc_client_api::{ + backend::{ + self, apply_aux, changes_tries_state_at_block, BlockImportOperation, ClientImportOperation, + Finalizer, ImportSummary, LockImportRun, NewBlockState, PrunableStateChangesTrieStorage, + StorageProvider, + }, + cht, + client::{ + BadBlocks, BlockBackend, BlockImportNotification, BlockOf, BlockchainEvents, ClientInfo, + FinalityNotification, FinalityNotifications, ForkBlocks, ImportNotifications, + ProvideUncles, + }, + execution_extensions::ExecutionExtensions, + notifications::{StorageEventStream, StorageNotifications}, + CallExecutor, ExecutorProvider, KeyIterator, ProofProvider, UsageProvider, +}; +use sc_executor::RuntimeVersion; +use sc_light::{call_executor::prove_execution, fetcher::ChangesProof}; +use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO}; +use sp_api::{ + ApiExt, ApiRef, CallApiAt, CallApiAtParams, ConstructRuntimeApi, Core as CoreApi, + ProvideRuntimeApi, +}; +use sp_blockchain::{ + self as blockchain, well_known_cache_keys::Id as CacheKeyId, Backend as ChainBackend, Cache, + CachedHeaderMetadata, Error, HeaderBackend as ChainHeaderBackend, HeaderMetadata, ProvideCache, +}; +use sp_consensus::{ + BlockCheckParams, BlockImportParams, BlockOrigin, BlockStatus, Error as ConsensusError, + ForkChoiceStrategy, ImportResult, StateAction, +}; use sp_core::{ convert_hash, storage::{well_known_keys, ChildInfo, PrefixedStorageKey, StorageData, StorageKey}, ChangesTrieConfiguration, ExecutionContext, NativeOrEncoded, }; -#[cfg(feature="test-helpers")] +#[cfg(feature = "test-helpers")] use sp_keystore::SyncCryptoStorePtr; -use sc_telemetry::{ - telemetry, - TelemetryHandle, - SUBSTRATE_INFO, -}; use sp_runtime::{ - Justification, Justifications, BuildStorage, - generic::{BlockId, SignedBlock, DigestItem}, + generic::{BlockId, DigestItem, SignedBlock}, traits::{ - Block as BlockT, Header as HeaderT, Zero, NumberFor, HashFor, SaturatedConversion, One, - DigestFor, + Block as BlockT, DigestFor, HashFor, Header as HeaderT, NumberFor, One, + SaturatedConversion, Zero, }, + BuildStorage, Justification, Justifications, }; use sp_state_machine::{ - DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, - prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, - ChangesTrieConfigurationRange, key_changes, key_changes_proof, - prove_range_read_with_size, read_range_proof_check, -}; -use sc_executor::RuntimeVersion; -use sp_consensus::{ - Error as ConsensusError, BlockStatus, BlockImportParams, BlockCheckParams, - ImportResult, BlockOrigin, ForkChoiceStrategy, StateAction, -}; -use sp_blockchain::{ - self as blockchain, - Backend as ChainBackend, - HeaderBackend as ChainHeaderBackend, ProvideCache, Cache, - well_known_cache_keys::Id as CacheKeyId, - HeaderMetadata, CachedHeaderMetadata, + key_changes, key_changes_proof, prove_child_read, prove_range_read_with_size, prove_read, + read_range_proof_check, Backend as StateBackend, ChangesTrieAnchorBlockId, + ChangesTrieConfigurationRange, ChangesTrieRootsStorage, ChangesTrieStorage, DBValue, }; use sp_trie::StorageProof; -use sp_api::{ - CallApiAt, ConstructRuntimeApi, Core as CoreApi, ApiExt, ApiRef, ProvideRuntimeApi, - CallApiAtParams, -}; -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof}; -use sc_client_api::{ - backend::{ - self, BlockImportOperation, PrunableStateChangesTrieStorage, - ClientImportOperation, Finalizer, ImportSummary, NewBlockState, - changes_tries_state_at_block, StorageProvider, - LockImportRun, apply_aux, - }, - client::{ - ImportNotifications, FinalityNotification, FinalityNotifications, BlockImportNotification, - ClientInfo, BlockchainEvents, BlockBackend, ProvideUncles, BadBlocks, ForkBlocks, - BlockOf, - }, - execution_extensions::ExecutionExtensions, - notifications::{StorageNotifications, StorageEventStream}, - KeyIterator, CallExecutor, ExecutorProvider, ProofProvider, - cht, UsageProvider, -}; -use sp_utils::mpsc::{TracingUnboundedSender, tracing_unbounded}; -use sp_blockchain::Error; -use prometheus_endpoint::Registry; -use super::{ - genesis, block_rules::{BlockRules, LookupResult as BlockLookupResult}, +use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + marker::PhantomData, + panic::UnwindSafe, + path::PathBuf, + result, + sync::Arc, }; -use sc_light::{call_executor::prove_execution, fetcher::ChangesProof}; -use rand::Rng; -#[cfg(feature="test-helpers")] +#[cfg(feature = "test-helpers")] use { - sp_core::traits::{CodeExecutor, SpawnNamed}, + super::call_executor::LocalCallExecutor, sc_client_api::in_mem, sc_executor::RuntimeInfo, - super::call_executor::LocalCallExecutor, + sp_core::traits::{CodeExecutor, SpawnNamed}, }; type NotificationSinks = Mutex>>; /// Substrate Client -pub struct Client where Block: BlockT { +pub struct Client +where + Block: BlockT, +{ backend: Arc, executor: E, storage_notifications: Mutex>, @@ -157,7 +153,7 @@ enum PrepareStorageChangesResult, Block: BlockT> { } /// Create an instance of in-memory client. -#[cfg(feature="test-helpers")] +#[cfg(feature = "test-helpers")] pub fn new_in_mem( executor: E, genesis_storage: &S, @@ -166,12 +162,10 @@ pub fn new_in_mem( telemetry: Option, spawn_handle: Box, config: ClientConfig, -) -> sp_blockchain::Result, - LocalCallExecutor, E>, - Block, - RA ->> where +) -> sp_blockchain::Result< + Client, LocalCallExecutor, E>, Block, RA>, +> +where E: CodeExecutor + RuntimeInfo, S: BuildStorage, Block: BlockT, @@ -218,7 +212,7 @@ impl Default for ClientConfig { /// Create a client with the explicitly provided backend. /// This is useful for testing backend implementations. -#[cfg(feature="test-helpers")] +#[cfg(feature = "test-helpers")] pub fn new_with_backend( backend: Arc, executor: E, @@ -229,13 +223,14 @@ pub fn new_with_backend( telemetry: Option, config: ClientConfig, ) -> sp_blockchain::Result, Block, RA>> - where - E: CodeExecutor + RuntimeInfo, - S: BuildStorage, - Block: BlockT, - B: backend::LocalBackend + 'static, +where + E: CodeExecutor + RuntimeInfo, + S: BuildStorage, + Block: BlockT, + B: backend::LocalBackend + 'static, { - let call_executor = LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?; + let call_executor = + LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?; let extensions = ExecutionExtensions::new( Default::default(), keystore, @@ -254,7 +249,8 @@ pub fn new_with_backend( ) } -impl BlockOf for Client where +impl BlockOf for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -263,15 +259,15 @@ impl BlockOf for Client where } impl LockImportRun for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, +where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, { fn lock_import_and_run(&self, f: F) -> Result - where - F: FnOnce(&mut ClientImportOperation) -> Result, - Err: From, + where + F: FnOnce(&mut ClientImportOperation) -> Result, + Err: From, { let inner = || { let _import_lock = self.backend.get_import_lock().write(); @@ -301,21 +297,22 @@ impl LockImportRun for Client } impl LockImportRun for &Client - where - Block: BlockT, - B: backend::Backend, - E: CallExecutor, +where + Block: BlockT, + B: backend::Backend, + E: CallExecutor, { fn lock_import_and_run(&self, f: F) -> Result - where - F: FnOnce(&mut ClientImportOperation) -> Result, - Err: From, + where + F: FnOnce(&mut ClientImportOperation) -> Result, + Err: From, { (**self).lock_import_and_run(f) } } -impl Client where +impl Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -335,12 +332,13 @@ impl Client where ) -> sp_blockchain::Result { let info = backend.blockchain().info(); if info.finalized_state.is_none() { - let genesis_storage = build_genesis_storage.build_storage() - .map_err(sp_blockchain::Error::Storage)?; + let genesis_storage = + build_genesis_storage.build_storage().map_err(sp_blockchain::Error::Storage)?; let mut op = backend.begin_operation()?; let state_root = op.set_genesis_state(genesis_storage, !config.no_genesis)?; let genesis_block = genesis::construct_genesis_block::(state_root.into()); - info!("🔨 Initializing Genesis block/state (state: {}, header-hash: {})", + info!( + "🔨 Initializing Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), genesis_block.header().hash() ); @@ -396,8 +394,11 @@ impl Client where /// Get the code at a given block. pub fn code_at(&self, id: &BlockId) -> sp_blockchain::Result> { Ok(StorageProvider::storage(self, id, &StorageKey(well_known_keys::CODE.to_vec()))? - .expect("None is returned if there's no value stored for the given key;\ - ':code' key is always defined; qed").0) + .expect( + "None is returned if there's no value stored for the given key;\ + ':code' key is always defined; qed", + ) + .0) } /// Get the RuntimeVersion at a given block. @@ -411,7 +412,9 @@ impl Client where id: &BlockId, cht_size: NumberFor, ) -> sp_blockchain::Result<(Block::Header, StorageProof)> { - let proof_error = || sp_blockchain::Error::Backend(format!("Failed to generate header proof for {:?}", id)); + let proof_error = || { + sp_blockchain::Error::Backend(format!("Failed to generate header proof for {:?}", id)) + }; let header = self.backend.blockchain().expect_header(*id)?; let block_num = *header.number(); let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?; @@ -449,12 +452,13 @@ impl Client where required_roots_proofs: Mutex, Block::Hash>>, } - impl<'a, Block: BlockT> ChangesTrieRootsStorage, NumberFor> for - AccessedRootsRecorder<'a, Block> + impl<'a, Block: BlockT> ChangesTrieRootsStorage, NumberFor> + for AccessedRootsRecorder<'a, Block> { - fn build_anchor(&self, hash: Block::Hash) - -> Result>, String> - { + fn build_anchor( + &self, + hash: Block::Hash, + ) -> Result>, String> { self.storage.build_anchor(hash) } @@ -466,22 +470,19 @@ impl Client where let root = self.storage.root(anchor, block)?; if block < self.min { if let Some(ref root) = root { - self.required_roots_proofs.lock().insert( - block, - root.clone() - ); + self.required_roots_proofs.lock().insert(block, root.clone()); } } Ok(root) } } - impl<'a, Block: BlockT> ChangesTrieStorage, NumberFor> for - AccessedRootsRecorder<'a, Block> + impl<'a, Block: BlockT> ChangesTrieStorage, NumberFor> + for AccessedRootsRecorder<'a, Block> { - fn as_roots_storage(&self) - -> &dyn sp_state_machine::ChangesTrieRootsStorage, NumberFor> - { + fn as_roots_storage( + &self, + ) -> &dyn sp_state_machine::ChangesTrieRootsStorage, NumberFor> { self } @@ -498,10 +499,11 @@ impl Client where } } - let first_number = self.backend.blockchain() - .expect_block_number_from_id(&BlockId::Hash(first))?; + let first_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(first))?; let (storage, configs) = self.require_changes_trie(first_number, last, true)?; - let min_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?; + let min_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?; let recording_storage = AccessedRootsRecorder:: { storage: storage.storage(), @@ -517,8 +519,8 @@ impl Client where // fetch key changes proof let mut proof = Vec::new(); for (config_zero, config_end, config) in configs { - let last_number = self.backend.blockchain() - .expect_block_number_from_id(&BlockId::Hash(last))?; + let last_number = + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(last))?; let config_range = ChangesTrieConfigurationRange { config: &config, zero: config_zero, @@ -528,10 +530,7 @@ impl Client where config_range, &recording_storage, first_number, - &ChangesTrieAnchorBlockId { - hash: convert_hash(&last), - number: last_number, - }, + &ChangesTrieAnchorBlockId { hash: convert_hash(&last), number: last_number }, max_number, storage_key, &key.0, @@ -554,20 +553,26 @@ impl Client where } /// Generate CHT-based proof for roots of changes tries at given blocks. - fn changes_trie_roots_proof>>( + fn changes_trie_roots_proof>>( &self, cht_size: NumberFor, - blocks: I + blocks: I, ) -> sp_blockchain::Result { // most probably we have touched several changes tries that are parts of the single CHT // => GroupBy changes tries by CHT number and then gather proof for the whole group at once let mut proofs = Vec::new(); - cht::for_each_cht_group::(cht_size, blocks, |_, cht_num, cht_blocks| { - let cht_proof = self.changes_trie_roots_proof_at_cht(cht_size, cht_num, cht_blocks)?; - proofs.push(cht_proof); - Ok(()) - }, ())?; + cht::for_each_cht_group::( + cht_size, + blocks, + |_, cht_num, cht_blocks| { + let cht_proof = + self.changes_trie_roots_proof_at_cht(cht_size, cht_num, cht_blocks)?; + proofs.push(cht_proof); + Ok(()) + }, + (), + )?; Ok(StorageProof::merge(proofs)) } @@ -577,7 +582,7 @@ impl Client where &self, cht_size: NumberFor, cht_num: NumberFor, - blocks: Vec> + blocks: Vec>, ) -> sp_blockchain::Result { let cht_start = cht::start_number(cht_size, cht_num); let mut current_num = cht_start; @@ -586,16 +591,14 @@ impl Client where current_num = current_num + One::one(); Some(old_current_num) }); - let roots = cht_range - .map(|num| self.header(&BlockId::Number(num)) - .map(|block| - block.and_then(|block| block.digest().log(DigestItem::as_changes_trie_root).cloned())) - ); + let roots = cht_range.map(|num| { + self.header(&BlockId::Number(num)).map(|block| { + block + .and_then(|block| block.digest().log(DigestItem::as_changes_trie_root).cloned()) + }) + }); let proof = cht::build_proof::, _, _>( - cht_size, - cht_num, - blocks, - roots, + cht_size, cht_num, blocks, roots, )?; Ok(proof) } @@ -616,7 +619,9 @@ impl Client where &dyn PrunableStateChangesTrieStorage, Vec<(NumberFor, Option<(NumberFor, Block::Hash)>, ChangesTrieConfiguration)>, )> { - let storage = self.backend.changes_trie_storage() + let storage = self + .backend + .changes_trie_storage() .ok_or_else(|| sp_blockchain::Error::ChangesTriesNotSupported)?; let mut configs = Vec::with_capacity(1); @@ -630,10 +635,14 @@ impl Client where } if config_range.zero.0 < first { - break; + break } - current = *self.backend.blockchain().expect_header(BlockId::Hash(config_range.zero.1))?.parent_hash(); + current = *self + .backend + .blockchain() + .expect_header(BlockId::Hash(config_range.zero.1))? + .parent_hash(); } Ok((storage, configs)) @@ -646,11 +655,14 @@ impl Client where operation: &mut ClientImportOperation, import_block: BlockImportParams>, new_cache: HashMap>, - storage_changes: Option>>, - ) -> sp_blockchain::Result where + storage_changes: Option< + sp_consensus::StorageChanges>, + >, + ) -> sp_blockchain::Result + where Self: ProvideRuntimeApi, - >::Api: CoreApi + - ApiExt, + >::Api: + CoreApi + ApiExt, { let BlockImportParams { origin, @@ -711,9 +723,7 @@ impl Client where // don't send telemetry block import events during initial sync for every // block to avoid spamming the telemetry server, these events will be randomly // sent at a rate of 1/10. - if origin != BlockOrigin::NetworkInitialSync || - rand::thread_rng().gen_bool(0.1) - { + if origin != BlockOrigin::NetworkInitialSync || rand::thread_rng().gen_bool(0.1) { telemetry!( self.telemetry; SUBSTRATE_INFO; @@ -738,25 +748,28 @@ impl Client where justifications: Option, body: Option>, indexed_body: Option>>, - storage_changes: Option>>, + storage_changes: Option< + sp_consensus::StorageChanges>, + >, new_cache: HashMap>, finalized: bool, aux: Vec<(Vec, Option>)>, fork_choice: ForkChoiceStrategy, import_existing: bool, - ) -> sp_blockchain::Result where + ) -> sp_blockchain::Result + where Self: ProvideRuntimeApi, - >::Api: CoreApi + - ApiExt, + >::Api: + CoreApi + ApiExt, { let parent_hash = import_headers.post().parent_hash().clone(); let status = self.backend.blockchain().status(BlockId::Hash(hash))?; - let parent_exists = self.backend.blockchain().status(BlockId::Hash(parent_hash))? - == blockchain::BlockStatus::InChain; + let parent_exists = self.backend.blockchain().status(BlockId::Hash(parent_hash))? == + blockchain::BlockStatus::InChain; match (import_existing, status) { (false, blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain), (false, blockchain::BlockStatus::Unknown) => {}, - (true, blockchain::BlockStatus::InChain) => {}, + (true, blockchain::BlockStatus::InChain) => {}, (true, blockchain::BlockStatus::Unknown) => {}, } @@ -764,17 +777,18 @@ impl Client where // the block is lower than our last finalized block so it must revert // finality, refusing import. - if status == blockchain::BlockStatus::Unknown - && *import_headers.post().number() <= info.finalized_number + if status == blockchain::BlockStatus::Unknown && + *import_headers.post().number() <= info.finalized_number { - return Err(sp_blockchain::Error::NotInFinalizedChain); + return Err(sp_blockchain::Error::NotInFinalizedChain) } // this is a fairly arbitrary choice of where to draw the line on making notifications, // but the general goal is to only make notifications when we are already fully synced // and get a new chain head. let make_notifications = match origin { - BlockOrigin::NetworkBroadcast | BlockOrigin::Own | BlockOrigin::ConsensusBroadcast => true, + BlockOrigin::NetworkBroadcast | BlockOrigin::Own | BlockOrigin::ConsensusBroadcast => + true, BlockOrigin::Genesis | BlockOrigin::NetworkInitialSync | BlockOrigin::File => false, }; @@ -782,15 +796,10 @@ impl Client where Some(storage_changes) => { let storage_changes = match storage_changes { sp_consensus::StorageChanges::Changes(storage_changes) => { - self.backend.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?; - let ( - main_sc, - child_sc, - offchain_sc, - tx, _, - changes_trie_tx, - tx_index, - ) = storage_changes.into_inner(); + self.backend + .begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?; + let (main_sc, child_sc, offchain_sc, tx, _, changes_trie_tx, tx_index) = + storage_changes.into_inner(); if self.config.offchain_indexing_api { operation.op.update_offchain_storage(offchain_sc)?; @@ -804,7 +813,7 @@ impl Client where operation.op.update_changes_trie(changes_trie_transaction)?; } Some((main_sc, child_sc)) - } + }, sp_consensus::StorageChanges::Import(changes) => { let storage = sp_storage::Storage { top: changes.state.into_iter().collect(), @@ -816,23 +825,24 @@ impl Client where // State root mismatch when importing state. This should not happen in safe fast sync mode, // but may happen in unsafe mode. warn!("Error imporing state: State root mismatch."); - return Err(Error::InvalidStateRoot); + return Err(Error::InvalidStateRoot) } None - } + }, }; operation.op.update_cache(new_cache); storage_changes - }, None => None, }; - let is_new_best = finalized || match fork_choice { - ForkChoiceStrategy::LongestChain => import_headers.post().number() > &info.best_number, - ForkChoiceStrategy::Custom(v) => v, - }; + let is_new_best = finalized || + match fork_choice { + ForkChoiceStrategy::LongestChain => + import_headers.post().number() > &info.best_number, + ForkChoiceStrategy::Custom(v) => v, + }; let leaf_state = if finalized { NewBlockState::Final @@ -843,11 +853,8 @@ impl Client where }; let tree_route = if is_new_best && info.best_hash != parent_hash && parent_exists { - let route_from_best = sp_blockchain::tree_route( - self.backend.blockchain(), - info.best_hash, - parent_hash, - )?; + let route_from_best = + sp_blockchain::tree_route(self.backend.blockchain(), info.best_hash, parent_hash)?; Some(route_from_best) } else { None @@ -899,20 +906,24 @@ impl Client where &self, import_block: &mut BlockImportParams>, ) -> sp_blockchain::Result> - where - Self: ProvideRuntimeApi, - >::Api: CoreApi + - ApiExt, + where + Self: ProvideRuntimeApi, + >::Api: + CoreApi + ApiExt, { let parent_hash = import_block.header.parent_hash(); let at = BlockId::Hash(*parent_hash); let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip); let (enact_state, storage_changes) = match (self.block_status(&at)?, state_action) { - (BlockStatus::KnownBad, _) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad)), - (BlockStatus::InChainPruned, StateAction::ApplyChanges(sp_consensus::StorageChanges::Changes(_))) => - return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), + (BlockStatus::KnownBad, _) => + return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad)), + ( + BlockStatus::InChainPruned, + StateAction::ApplyChanges(sp_consensus::StorageChanges::Changes(_)), + ) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), (_, StateAction::ApplyChanges(changes)) => (true, Some(changes)), - (BlockStatus::Unknown, _) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)), + (BlockStatus::Unknown, _) => + return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)), (_, StateAction::Skip) => (false, None), (BlockStatus::InChainPruned, StateAction::Execute) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)), @@ -942,19 +953,14 @@ impl Client where )?; let state = self.backend.state_at(at)?; - let changes_trie_state = changes_tries_state_at_block( - &at, - self.backend.changes_trie_storage(), - )?; + let changes_trie_state = + changes_tries_state_at_block(&at, self.backend.changes_trie_storage())?; - let gen_storage_changes = runtime_api.into_storage_changes( - &state, - changes_trie_state.as_ref(), - *parent_hash, - ).map_err(sp_blockchain::Error::Storage)?; + let gen_storage_changes = runtime_api + .into_storage_changes(&state, changes_trie_state.as_ref(), *parent_hash) + .map_err(sp_blockchain::Error::Storage)?; - if import_block.header.state_root() - != &gen_storage_changes.transaction_storage_root + if import_block.header.state_root() != &gen_storage_changes.transaction_storage_root { return Err(Error::InvalidStateRoot) } @@ -981,20 +987,28 @@ impl Client where let last_finalized = self.backend.blockchain().last_finalized()?; if block == last_finalized { - warn!("Possible safety violation: attempted to re-finalize last finalized block {:?} ", last_finalized); - return Ok(()); + warn!( + "Possible safety violation: attempted to re-finalize last finalized block {:?} ", + last_finalized + ); + return Ok(()) } - let route_from_finalized = sp_blockchain::tree_route(self.backend.blockchain(), last_finalized, block)?; + let route_from_finalized = + sp_blockchain::tree_route(self.backend.blockchain(), last_finalized, block)?; if let Some(retracted) = route_from_finalized.retracted().get(0) { - warn!("Safety violation: attempted to revert finalized block {:?} which is not in the \ - same chain as last finalized {:?}", retracted, last_finalized); + warn!( + "Safety violation: attempted to revert finalized block {:?} which is not in the \ + same chain as last finalized {:?}", + retracted, last_finalized + ); - return Err(sp_blockchain::Error::NotInFinalizedChain); + return Err(sp_blockchain::Error::NotInFinalizedChain) } - let route_from_best = sp_blockchain::tree_route(self.backend.blockchain(), best_block, block)?; + let route_from_best = + sp_blockchain::tree_route(self.backend.blockchain(), best_block, block)?; // if the block is not a direct ancestor of the current best chain, // then some other block is the common ancestor. @@ -1031,10 +1045,7 @@ impl Client where Ok(()) } - fn notify_finalized( - &self, - notify_finalized: Vec, - ) -> sp_blockchain::Result<()> { + fn notify_finalized(&self, notify_finalized: Vec) -> sp_blockchain::Result<()> { let mut sinks = self.finality_notification_sinks.lock(); if notify_finalized.is_empty() { @@ -1043,17 +1054,16 @@ impl Client where // would also remove any closed sinks. sinks.retain(|sink| !sink.is_closed()); - return Ok(()); + return Ok(()) } // We assume the list is sorted and only want to inform the // telemetry once about the finalized block. if let Some(last) = notify_finalized.last() { - let header = self.header(&BlockId::Hash(*last))? - .expect( - "Header already known to exist in DB because it is \ - indicated in the tree route; qed" - ); + let header = self.header(&BlockId::Hash(*last))?.expect( + "Header already known to exist in DB because it is \ + indicated in the tree route; qed", + ); telemetry!( self.telemetry; @@ -1065,16 +1075,12 @@ impl Client where } for finalized_hash in notify_finalized { - let header = self.header(&BlockId::Hash(finalized_hash))? - .expect( - "Header already known to exist in DB because it is \ - indicated in the tree route; qed" - ); + let header = self.header(&BlockId::Hash(finalized_hash))?.expect( + "Header already known to exist in DB because it is \ + indicated in the tree route; qed", + ); - let notification = FinalityNotification { - header, - hash: finalized_hash, - }; + let notification = FinalityNotification { header, hash: finalized_hash }; sinks.retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); } @@ -1095,22 +1101,19 @@ impl Client where // won't send any import notifications which could lead to a // temporary leak of closed/discarded notification sinks (e.g. // from consensus code). - self.import_notification_sinks - .lock() - .retain(|sink| !sink.is_closed()); + self.import_notification_sinks.lock().retain(|sink| !sink.is_closed()); - return Ok(()); - } + return Ok(()) + }, }; if let Some(storage_changes) = notify_import.storage_changes { // TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes? - self.storage_notifications.lock() - .trigger( - ¬ify_import.hash, - storage_changes.0.into_iter(), - storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), - ); + self.storage_notifications.lock().trigger( + ¬ify_import.hash, + storage_changes.0.into_iter(), + storage_changes.1.into_iter().map(|(sk, v)| (sk, v.into_iter())), + ); } let notification = BlockImportNotification:: { @@ -1121,7 +1124,8 @@ impl Client where tree_route: notify_import.tree_route.map(Arc::new), }; - self.import_notification_sinks.lock() + self.import_notification_sinks + .lock() .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); Ok(()) @@ -1168,7 +1172,7 @@ impl Client where // this can probably be implemented more efficiently if let BlockId::Hash(ref h) = id { if self.importing_block.read().as_ref().map_or(false, |importing| h == importing) { - return Ok(BlockStatus::Queued); + return Ok(BlockStatus::Queued) } } let hash_and_number = match id.clone() { @@ -1176,24 +1180,29 @@ impl Client where BlockId::Number(n) => self.backend.blockchain().hash(n)?.map(|hash| (hash, n)), }; match hash_and_number { - Some((hash, number)) => { + Some((hash, number)) => if self.backend.have_state_at(&hash, number) { Ok(BlockStatus::InChainWithState) } else { Ok(BlockStatus::InChainPruned) - } - } + }, None => Ok(BlockStatus::Unknown), } } /// Get block header by id. - pub fn header(&self, id: &BlockId) -> sp_blockchain::Result::Header>> { + pub fn header( + &self, + id: &BlockId, + ) -> sp_blockchain::Result::Header>> { self.backend.blockchain().header(*id) } /// Get block body by id. - pub fn body(&self, id: &BlockId) -> sp_blockchain::Result::Extrinsic>>> { + pub fn body( + &self, + id: &BlockId, + ) -> sp_blockchain::Result::Extrinsic>>> { self.backend.blockchain().body(*id) } @@ -1204,13 +1213,15 @@ impl Client where max_generation: NumberFor, ) -> sp_blockchain::Result> { let load_header = |id: Block::Hash| -> sp_blockchain::Result { - self.backend.blockchain().header(BlockId::Hash(id))? + self.backend + .blockchain() + .header(BlockId::Hash(id))? .ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))) }; let genesis_hash = self.backend.blockchain().info().genesis_hash; if genesis_hash == target_hash { - return Ok(Vec::new()); + return Ok(Vec::new()) } let mut current_hash = target_hash; @@ -1226,7 +1237,7 @@ impl Client where current_hash = ancestor_hash; if genesis_hash == current_hash { - break; + break } current = ancestor; @@ -1239,21 +1250,20 @@ impl Client where } } -impl UsageProvider for Client where +impl UsageProvider for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, { /// Get usage info about current client. fn usage_info(&self) -> ClientInfo { - ClientInfo { - chain: self.chain_info(), - usage: self.backend.usage_info(), - } + ClientInfo { chain: self.chain_info(), usage: self.backend.usage_info() } } } -impl ProofProvider for Client where +impl ProofProvider for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -1261,29 +1271,26 @@ impl ProofProvider for Client where fn read_proof( &self, id: &BlockId, - keys: &mut dyn Iterator, + keys: &mut dyn Iterator, ) -> sp_blockchain::Result { - self.state_at(id) - .and_then(|state| prove_read(state, keys) - .map_err(Into::into)) + self.state_at(id).and_then(|state| prove_read(state, keys).map_err(Into::into)) } fn read_child_proof( &self, id: &BlockId, child_info: &ChildInfo, - keys: &mut dyn Iterator, + keys: &mut dyn Iterator, ) -> sp_blockchain::Result { self.state_at(id) - .and_then(|state| prove_child_read(state, child_info, keys) - .map_err(Into::into)) + .and_then(|state| prove_child_read(state, child_info, keys).map_err(Into::into)) } fn execution_proof( &self, id: &BlockId, method: &str, - call_data: &[u8] + call_data: &[u8], ) -> sp_blockchain::Result<(Vec, StorageProof)> { // Make sure we include the `:code` and `:heap_pages` in the execution proof to be // backwards compatible. @@ -1295,17 +1302,14 @@ impl ProofProvider for Client where )?; let state = self.state_at(id)?; - prove_execution( - state, - &self.executor, - method, - call_data, - ).map(|(r, p)| { - (r, StorageProof::merge(vec![p, code_proof])) - }) + prove_execution(state, &self.executor, method, call_data) + .map(|(r, p)| (r, StorageProof::merge(vec![p, code_proof]))) } - fn header_proof(&self, id: &BlockId) -> sp_blockchain::Result<(Block::Header, StorageProof)> { + fn header_proof( + &self, + id: &BlockId, + ) -> sp_blockchain::Result<(Block::Header, StorageProof)> { self.header_proof_with_cht_size(id, cht::size()) } @@ -1318,15 +1322,7 @@ impl ProofProvider for Client where storage_key: Option<&PrefixedStorageKey>, key: &StorageKey, ) -> sp_blockchain::Result> { - self.key_changes_proof_with_cht_size( - first, - last, - min, - max, - storage_key, - key, - cht::size(), - ) + self.key_changes_proof_with_cht_size(first, last, min, max, storage_key, key, cht::size()) } fn read_proof_collection( @@ -1337,11 +1333,11 @@ impl ProofProvider for Client where ) -> sp_blockchain::Result<(StorageProof, u32)> { let state = self.state_at(id)?; Ok(prove_range_read_with_size::<_, HashFor>( - state, - None, - None, - size_limit, - Some(start_key) + state, + None, + None, + size_limit, + Some(start_key), )?) } @@ -1365,14 +1361,13 @@ impl ProofProvider for Client where .unwrap_or_default(); let size = value.len() + next_key.len(); if total_size + size > size_limit && !entries.is_empty() { - break; + break } total_size += size; entries.push((next_key.clone(), value)); current_key = next_key; } Ok(entries) - } fn verify_range_proof( @@ -1382,25 +1377,24 @@ impl ProofProvider for Client where start_key: &[u8], ) -> sp_blockchain::Result<(Vec<(Vec, Vec)>, bool)> { Ok(read_range_proof_check::>( - root, - proof, - None, - None, - None, - Some(start_key), + root, + proof, + None, + None, + None, + Some(start_key), )?) } } - impl BlockBuilderProvider for Client - where - B: backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static, - Block: BlockT, - Self: ChainHeaderBackend + ProvideRuntimeApi, - >::Api: ApiExt> - + BlockBuilderApi, +where + B: backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + 'static, + Block: BlockT, + Self: ChainHeaderBackend + ProvideRuntimeApi, + >::Api: + ApiExt> + BlockBuilderApi, { fn new_block_at>( &self, @@ -1414,7 +1408,7 @@ impl BlockBuilderProvider for Client BlockBuilderProvider for Client ExecutorProvider for Client where +impl ExecutorProvider for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -1450,19 +1445,26 @@ impl ExecutorProvider for Client where } } -impl StorageProvider for Client where +impl StorageProvider for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, { - fn storage_keys(&self, id: &BlockId, key_prefix: &StorageKey) -> sp_blockchain::Result> { + fn storage_keys( + &self, + id: &BlockId, + key_prefix: &StorageKey, + ) -> sp_blockchain::Result> { let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); Ok(keys) } - fn storage_pairs(&self, id: &BlockId, key_prefix: &StorageKey) - -> sp_blockchain::Result> - { + fn storage_pairs( + &self, + id: &BlockId, + key_prefix: &StorageKey, + ) -> sp_blockchain::Result> { let state = self.state_at(id)?; let keys = state .keys(&key_prefix.0) @@ -1479,13 +1481,10 @@ impl StorageProvider for Client wher &self, id: &BlockId, prefix: Option<&'a StorageKey>, - start_key: Option<&StorageKey> + start_key: Option<&StorageKey>, ) -> sp_blockchain::Result> { let state = self.state_at(id)?; - let start_key = start_key - .or(prefix) - .map(|key| key.0.clone()) - .unwrap_or_else(Vec::new); + let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); Ok(KeyIterator::new(state, prefix, start_key)) } @@ -1494,13 +1493,10 @@ impl StorageProvider for Client wher id: &BlockId, child_info: ChildInfo, prefix: Option<&'a StorageKey>, - start_key: Option<&StorageKey> + start_key: Option<&StorageKey>, ) -> sp_blockchain::Result> { let state = self.state_at(id)?; - let start_key = start_key - .or(prefix) - .map(|key| key.0.clone()) - .unwrap_or_else(Vec::new); + let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); Ok(KeyIterator::new_child(state, child_info, prefix, start_key)) } @@ -1509,30 +1505,32 @@ impl StorageProvider for Client wher id: &BlockId, key: &StorageKey, ) -> sp_blockchain::Result> { - Ok(self.state_at(id)? - .storage(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? - .map(StorageData) - ) + Ok(self + .state_at(id)? + .storage(&key.0) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? + .map(StorageData)) } - fn storage_hash( &self, id: &BlockId, key: &StorageKey, ) -> sp_blockchain::Result> { - Ok(self.state_at(id)? - .storage_hash(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? - ) + Ok(self + .state_at(id)? + .storage_hash(&key.0) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?) } fn child_storage_keys( &self, id: &BlockId, child_info: &ChildInfo, - key_prefix: &StorageKey + key_prefix: &StorageKey, ) -> sp_blockchain::Result> { - let keys = self.state_at(id)? + let keys = self + .state_at(id)? .child_keys(child_info, &key_prefix.0) .into_iter() .map(StorageKey) @@ -1544,9 +1542,10 @@ impl StorageProvider for Client wher &self, id: &BlockId, child_info: &ChildInfo, - key: &StorageKey + key: &StorageKey, ) -> sp_blockchain::Result> { - Ok(self.state_at(id)? + Ok(self + .state_at(id)? .child_storage(child_info, &key.0) .map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? .map(StorageData)) @@ -1556,12 +1555,12 @@ impl StorageProvider for Client wher &self, id: &BlockId, child_info: &ChildInfo, - key: &StorageKey + key: &StorageKey, ) -> sp_blockchain::Result> { - Ok(self.state_at(id)? + Ok(self + .state_at(id)? .child_storage_hash(child_info, &key.0) - .map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? - ) + .map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?) } fn max_key_changes_range( @@ -1572,7 +1571,9 @@ impl StorageProvider for Client wher let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?; let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?; if first > last_number { - return Err(sp_blockchain::Error::ChangesTrieAccessFailed("Invalid changes trie range".into())); + return Err(sp_blockchain::Error::ChangesTrieAccessFailed( + "Invalid changes trie range".into(), + )) } let (storage, configs) = match self.require_changes_trie(first, last_hash, false).ok() { @@ -1587,7 +1588,7 @@ impl StorageProvider for Client wher let first = std::cmp::max(first_available_changes_trie, oldest_unpruned); Ok(Some((first, last))) }, - None => Ok(None) + None => Ok(None), } } @@ -1596,7 +1597,7 @@ impl StorageProvider for Client wher first: NumberFor, last: BlockId, storage_key: Option<&PrefixedStorageKey>, - key: &StorageKey + key: &StorageKey, ) -> sp_blockchain::Result, u32)>> { let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?; let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?; @@ -1607,12 +1608,20 @@ impl StorageProvider for Client wher for (config_zero, config_end, config) in configs { let range_first = ::std::cmp::max(first, config_zero + One::one()); let range_anchor = match config_end { - Some((config_end_number, config_end_hash)) => if last_number > config_end_number { - ChangesTrieAnchorBlockId { hash: config_end_hash, number: config_end_number } - } else { - ChangesTrieAnchorBlockId { hash: convert_hash(&last_hash), number: last_number } - }, - None => ChangesTrieAnchorBlockId { hash: convert_hash(&last_hash), number: last_number }, + Some((config_end_number, config_end_hash)) => + if last_number > config_end_number { + ChangesTrieAnchorBlockId { + hash: config_end_hash, + number: config_end_number, + } + } else { + ChangesTrieAnchorBlockId { + hash: convert_hash(&last_hash), + number: last_number, + } + }, + None => + ChangesTrieAnchorBlockId { hash: convert_hash(&last_hash), number: last_number }, }; let config_range = ChangesTrieConfigurationRange { @@ -1627,9 +1636,10 @@ impl StorageProvider for Client wher &range_anchor, best_number, storage_key, - &key.0) - .and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::>()) - .map_err(|err| sp_blockchain::Error::ChangesTrieAccessFailed(err))?; + &key.0, + ) + .and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::>()) + .map_err(|err| sp_blockchain::Error::ChangesTrieAccessFailed(err))?; result.extend(result_range); } @@ -1637,14 +1647,18 @@ impl StorageProvider for Client wher } } -impl HeaderMetadata for Client where +impl HeaderMetadata for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, { type Error = sp_blockchain::Error; - fn header_metadata(&self, hash: Block::Hash) -> Result, Self::Error> { + fn header_metadata( + &self, + hash: Block::Hash, + ) -> Result, Self::Error> { self.backend.blockchain().header_metadata(hash) } @@ -1657,21 +1671,26 @@ impl HeaderMetadata for Client where } } -impl ProvideUncles for Client where +impl ProvideUncles for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, { - fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor) -> sp_blockchain::Result> { + fn uncles( + &self, + target_hash: Block::Hash, + max_generation: NumberFor, + ) -> sp_blockchain::Result> { Ok(Client::uncles(self, target_hash, max_generation)? .into_iter() .filter_map(|hash| Client::header(self, &BlockId::Hash(hash)).unwrap_or(None)) - .collect() - ) + .collect()) } } -impl ChainHeaderBackend for Client where +impl ChainHeaderBackend for Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, @@ -1689,7 +1708,10 @@ impl ChainHeaderBackend for Client wher self.backend.blockchain().status(id) } - fn number(&self, hash: Block::Hash) -> sp_blockchain::Result::Header as HeaderT>::Number>> { + fn number( + &self, + hash: Block::Hash, + ) -> sp_blockchain::Result::Header as HeaderT>::Number>> { self.backend.blockchain().number(hash) } @@ -1698,7 +1720,8 @@ impl ChainHeaderBackend for Client wher } } -impl sp_runtime::traits::BlockIdTo for Client where +impl sp_runtime::traits::BlockIdTo for Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, @@ -1710,12 +1733,16 @@ impl sp_runtime::traits::BlockIdTo for Client) -> sp_blockchain::Result>> { + fn to_number( + &self, + block_id: &BlockId, + ) -> sp_blockchain::Result>> { self.block_number_from_id(block_id) } } -impl ChainHeaderBackend for &Client where +impl ChainHeaderBackend for &Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, @@ -1733,7 +1760,10 @@ impl ChainHeaderBackend for &Client whe (**self).status(id) } - fn number(&self, hash: Block::Hash) -> sp_blockchain::Result::Header as HeaderT>::Number>> { + fn number( + &self, + hash: Block::Hash, + ) -> sp_blockchain::Result::Header as HeaderT>::Number>> { (**self).number(hash) } @@ -1742,7 +1772,8 @@ impl ChainHeaderBackend for &Client whe } } -impl ProvideCache for Client where +impl ProvideCache for Client +where B: backend::Backend, Block: BlockT, { @@ -1751,7 +1782,8 @@ impl ProvideCache for Client where } } -impl ProvideRuntimeApi for Client where +impl ProvideRuntimeApi for Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, @@ -1764,7 +1796,8 @@ impl ProvideRuntimeApi for Client where } } -impl CallApiAt for Client where +impl CallApiAt for Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, @@ -1781,28 +1814,25 @@ impl CallApiAt for Client where ) -> Result, sp_api::ApiError> { let at = params.at; - let (manager, extensions) = self.execution_extensions.manager_and_extensions( - at, - params.context, - ); - - self.executor.contextual_call:: _, _, _>( - at, - params.function, - ¶ms.arguments, - params.overlayed_changes, - Some(params.storage_transaction_cache), - manager, - params.native_call, - params.recorder, - Some(extensions), - ).map_err(Into::into) + let (manager, extensions) = + self.execution_extensions.manager_and_extensions(at, params.context); + + self.executor + .contextual_call:: _, _, _>( + at, + params.function, + ¶ms.arguments, + params.overlayed_changes, + Some(params.storage_transaction_cache), + manager, + params.native_call, + params.recorder, + Some(extensions), + ) + .map_err(Into::into) } - fn runtime_version_at( - &self, - at: &BlockId, - ) -> Result { + fn runtime_version_at(&self, at: &BlockId) -> Result { self.runtime_version_at(at).map_err(Into::into) } } @@ -1811,13 +1841,14 @@ impl CallApiAt for Client where /// objects. Otherwise, importing blocks directly into the client would be bypassing /// important verification work. #[async_trait::async_trait] -impl sp_consensus::BlockImport for &Client where +impl sp_consensus::BlockImport for &Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: CoreApi + - ApiExt, + as ProvideRuntimeApi>::Api: + CoreApi + ApiExt, RA: Sync + Send, backend::TransactionFor: Send + 'static, { @@ -1841,17 +1872,19 @@ impl sp_consensus::BlockImport for &Client return Ok(res), - PrepareStorageChangesResult::Import(storage_changes) => storage_changes, - }; + let storage_changes = + match self.prepare_block_storage_changes(&mut import_block).map_err(|e| { + warn!("Block prepare storage changes error:\n{:?}", e); + ConsensusError::ClientImport(e.to_string()) + })? { + PrepareStorageChangesResult::Discard(res) => return Ok(res), + PrepareStorageChangesResult::Import(storage_changes) => storage_changes, + }; self.lock_import_and_run(|operation| { self.apply_block(operation, import_block, new_cache, storage_changes) - }).map_err(|e| { + }) + .map_err(|e| { warn!("Block import error:\n{:?}", e); ConsensusError::ClientImport(e.to_string()).into() }) @@ -1875,12 +1908,8 @@ impl sp_consensus::BlockImport for &Client { - trace!( - "Rejecting known bad block: #{} {:?}", - number, - hash, - ); - return Ok(ImportResult::KnownBad); + trace!("Rejecting known bad block: #{} {:?}", number, hash,); + return Ok(ImportResult::KnownBad) }, BlockLookupResult::Expected(expected_hash) => { trace!( @@ -1889,52 +1918,52 @@ impl sp_consensus::BlockImport for &Client {} + BlockLookupResult::NotSpecial => {}, } // Own status must be checked first. If the block and ancestry is pruned // this function must return `AlreadyInChain` rather than `MissingState` - match self.block_status(&BlockId::Hash(hash)) + match self + .block_status(&BlockId::Hash(hash)) .map_err(|e| ConsensusError::ClientImport(e.to_string()))? { - BlockStatus::InChainWithState | BlockStatus::Queued if !import_existing => { - return Ok(ImportResult::AlreadyInChain) - }, + BlockStatus::InChainWithState | BlockStatus::Queued if !import_existing => + return Ok(ImportResult::AlreadyInChain), BlockStatus::InChainWithState | BlockStatus::Queued => {}, - BlockStatus::InChainPruned if !import_existing => { - return Ok(ImportResult::AlreadyInChain) - }, + BlockStatus::InChainPruned if !import_existing => + return Ok(ImportResult::AlreadyInChain), BlockStatus::InChainPruned => {}, BlockStatus::Unknown => {}, BlockStatus::KnownBad => return Ok(ImportResult::KnownBad), } - match self.block_status(&BlockId::Hash(parent_hash)) + match self + .block_status(&BlockId::Hash(parent_hash)) .map_err(|e| ConsensusError::ClientImport(e.to_string()))? - { - BlockStatus::InChainWithState | BlockStatus::Queued => {}, - BlockStatus::Unknown if allow_missing_parent => {}, - BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), - BlockStatus::InChainPruned if allow_missing_state => {}, - BlockStatus::InChainPruned => return Ok(ImportResult::MissingState), - BlockStatus::KnownBad => return Ok(ImportResult::KnownBad), - } - + { + BlockStatus::InChainWithState | BlockStatus::Queued => {}, + BlockStatus::Unknown if allow_missing_parent => {}, + BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), + BlockStatus::InChainPruned if allow_missing_state => {}, + BlockStatus::InChainPruned => return Ok(ImportResult::MissingState), + BlockStatus::KnownBad => return Ok(ImportResult::KnownBad), + } Ok(ImportResult::imported(false)) } } #[async_trait::async_trait] -impl sp_consensus::BlockImport for Client where +impl sp_consensus::BlockImport for Client +where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, Self: ProvideRuntimeApi, - >::Api: CoreApi + - ApiExt, + >::Api: + CoreApi + ApiExt, RA: Sync + Send, backend::TransactionFor: Send + 'static, { @@ -1957,7 +1986,8 @@ impl sp_consensus::BlockImport for Client Finalizer for Client where +impl Finalizer for Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -1992,8 +2022,8 @@ impl Finalizer for Client where } } - -impl Finalizer for &Client where +impl Finalizer for &Client +where B: backend::Backend, E: CallExecutor, Block: BlockT, @@ -2047,10 +2077,10 @@ where } impl BlockBackend for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, +where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, { fn block_body( &self, @@ -2089,35 +2119,37 @@ impl BlockBackend for Client fn block_indexed_body( &self, - id: &BlockId + id: &BlockId, ) -> sp_blockchain::Result>>> { self.backend.blockchain().block_indexed_body(*id) } } impl backend::AuxStore for Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, - Self: ProvideRuntimeApi, - >::Api: CoreApi, +where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, + Self: ProvideRuntimeApi, + >::Api: CoreApi, { /// Insert auxiliary data into key-value store. fn insert_aux< 'a, 'b: 'a, 'c: 'a, - I: IntoIterator, - D: IntoIterator, - >(&self, insert: I, delete: D) -> sp_blockchain::Result<()> { + I: IntoIterator, + D: IntoIterator, + >( + &self, + insert: I, + delete: D, + ) -> sp_blockchain::Result<()> { // Import is locked here because we may have other block import // operations that tries to set aux data. Note that for consensus // layer, one can always use atomic operations to make sure // import is only locked once. - self.lock_import_and_run(|operation| { - apply_aux(operation, insert, delete) - }) + self.lock_import_and_run(|operation| apply_aux(operation, insert, delete)) } /// Query auxiliary data from key-value store. fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result>> { @@ -2126,20 +2158,24 @@ impl backend::AuxStore for Client } impl backend::AuxStore for &Client - where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: CoreApi, +where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, + Client: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: CoreApi, { fn insert_aux< 'a, 'b: 'a, 'c: 'a, - I: IntoIterator, - D: IntoIterator, - >(&self, insert: I, delete: D) -> sp_blockchain::Result<()> { + I: IntoIterator, + D: IntoIterator, + >( + &self, + insert: I, + delete: D, + ) -> sp_blockchain::Result<()> { (**self).insert_aux(insert, delete) } @@ -2149,10 +2185,10 @@ impl backend::AuxStore for &Client } impl sp_consensus::block_validation::Chain for Client - where - BE: backend::Backend, - E: CallExecutor, - B: BlockT, +where + BE: backend::Backend, + E: CallExecutor, + B: BlockT, { fn block_status( &self, @@ -2171,8 +2207,10 @@ where fn block_indexed_body( &self, number: NumberFor, - ) ->Result>>, sp_transaction_storage_proof::Error> { - self.backend.blockchain().block_indexed_body(BlockId::number(number)) + ) -> Result>>, sp_transaction_storage_proof::Error> { + self.backend + .blockchain() + .block_indexed_body(BlockId::number(number)) .map_err(|e| sp_transaction_storage_proof::Error::Application(Box::new(e))) } @@ -2180,7 +2218,9 @@ where &self, hash: B::Hash, ) -> Result>, sp_transaction_storage_proof::Error> { - self.backend.blockchain().number(hash) + self.backend + .blockchain() + .number(hash) .map_err(|e| sp_transaction_storage_proof::Error::Application(Box::new(e))) } } diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index 0642f1d3bbc48..cce5aabcf1b6c 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -28,14 +28,17 @@ use std::collections::HashMap; -use sp_runtime::{Justifications, traits::{Block as BlockT, Header as _, NumberFor}}; +use sp_runtime::{ + traits::{Block as BlockT, Header as _, NumberFor}, + Justifications, +}; use crate::{ - error::Error as ConsensusError, block_import::{ - BlockImport, BlockOrigin, BlockImportParams, ImportedAux, JustificationImport, ImportResult, - BlockCheckParams, ImportedState, StateAction, + BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, ImportResult, ImportedAux, + ImportedState, JustificationImport, StateAction, }, + error::Error as ConsensusError, metrics::Metrics, }; pub use basic_queue::BasicQueue; @@ -43,18 +46,19 @@ pub use basic_queue::BasicQueue; /// A commonly-used Import Queue type. /// /// This defines the transaction type of the `BasicQueue` to be the transaction type for a client. -pub type DefaultImportQueue = BasicQueue>; +pub type DefaultImportQueue = + BasicQueue>; mod basic_queue; pub mod buffered_link; /// Shared block import struct used by the queue. -pub type BoxBlockImport = Box< - dyn BlockImport + Send + Sync ->; +pub type BoxBlockImport = + Box + Send + Sync>; /// Shared justification import struct used by the queue. -pub type BoxJustificationImport = Box + Send + Sync>; +pub type BoxJustificationImport = + Box + Send + Sync>; /// Maps to the Origin used by the network. pub type Origin = libp2p::PeerId; @@ -112,7 +116,7 @@ pub trait ImportQueue: Send { who: Origin, hash: B::Hash, number: NumberFor, - justifications: Justifications + justifications: Justifications, ); /// Polls for actions to perform on the network. /// @@ -130,10 +134,18 @@ pub trait Link: Send { &mut self, _imported: usize, _count: usize, - _results: Vec<(Result>, BlockImportError>, B::Hash)> - ) {} + _results: Vec<(Result>, BlockImportError>, B::Hash)>, + ) { + } /// Justification import result. - fn justification_imported(&mut self, _who: Origin, _hash: &B::Hash, _number: NumberFor, _success: bool) {} + fn justification_imported( + &mut self, + _who: Origin, + _hash: &B::Hash, + _number: NumberFor, + _success: bool, + ) { + } /// Request a justification for the given block. fn request_justification(&mut self, _hash: &B::Hash, _number: NumberFor) {} } @@ -177,7 +189,11 @@ pub async fn import_single_block, Transaction: Send + } /// Single block import function with metering. -pub(crate) async fn import_single_block_metered, Transaction: Send + 'static>( +pub(crate) async fn import_single_block_metered< + B: BlockT, + V: Verifier, + Transaction: Send + 'static, +>( import_handle: &mut impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, @@ -204,40 +220,43 @@ pub(crate) async fn import_single_block_metered, Trans let hash = block.hash; let parent_hash = header.parent_hash().clone(); - let import_handler = |import| { - match import { - Ok(ImportResult::AlreadyInChain) => { - trace!(target: "sync", "Block already in chain {}: {:?}", number, hash); - Ok(BlockImportResult::ImportedKnown(number, peer.clone())) - }, - Ok(ImportResult::Imported(aux)) => Ok(BlockImportResult::ImportedUnknown(number, aux, peer.clone())), - Ok(ImportResult::MissingState) => { - debug!(target: "sync", "Parent state is missing for {}: {:?}, parent: {:?}", number, hash, parent_hash); - Err(BlockImportError::MissingState) - }, - Ok(ImportResult::UnknownParent) => { - debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent_hash); - Err(BlockImportError::UnknownParent) - }, - Ok(ImportResult::KnownBad) => { - debug!(target: "sync", "Peer gave us a bad block {}: {:?}", number, hash); - Err(BlockImportError::BadBlock(peer.clone())) - }, - Err(e) => { - debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e); - Err(BlockImportError::Other(e)) - } - } + let import_handler = |import| match import { + Ok(ImportResult::AlreadyInChain) => { + trace!(target: "sync", "Block already in chain {}: {:?}", number, hash); + Ok(BlockImportResult::ImportedKnown(number, peer.clone())) + }, + Ok(ImportResult::Imported(aux)) => + Ok(BlockImportResult::ImportedUnknown(number, aux, peer.clone())), + Ok(ImportResult::MissingState) => { + debug!(target: "sync", "Parent state is missing for {}: {:?}, parent: {:?}", number, hash, parent_hash); + Err(BlockImportError::MissingState) + }, + Ok(ImportResult::UnknownParent) => { + debug!(target: "sync", "Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent_hash); + Err(BlockImportError::UnknownParent) + }, + Ok(ImportResult::KnownBad) => { + debug!(target: "sync", "Peer gave us a bad block {}: {:?}", number, hash); + Err(BlockImportError::BadBlock(peer.clone())) + }, + Err(e) => { + debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e); + Err(BlockImportError::Other(e)) + }, }; - match import_handler(import_handle.check_block(BlockCheckParams { - hash, - number, - parent_hash, - allow_missing_state: block.allow_missing_state, - import_existing: block.import_existing, - allow_missing_parent: block.state.is_some(), - }).await)? { + match import_handler( + import_handle + .check_block(BlockCheckParams { + hash, + number, + parent_hash, + allow_missing_state: block.allow_missing_state, + import_existing: block.import_existing, + allow_missing_parent: block.state.is_some(), + }) + .await, + )? { BlockImportResult::ImportedUnknown { .. } => (), r => return Ok(r), // Any other successful result means that the block is already imported. } @@ -250,7 +269,7 @@ pub(crate) async fn import_single_block_metered, Trans import_block.post_hash = Some(hash); import_block.import_existing = block.import_existing; import_block.indexed_body = block.indexed_body; - + if let Some(state) = block.state { import_block.state_action = StateAction::ApplyChanges(crate::StorageChanges::Import(state)); } else if block.skip_execution { @@ -259,9 +278,7 @@ pub(crate) async fn import_single_block_metered, Trans import_block.state_action = StateAction::ExecuteIfPossible; } - let (import_block, maybe_keys) = verifier.verify( - import_block, - ).await.map_err(|msg| { + let (import_block, maybe_keys) = verifier.verify(import_block).await.map_err(|msg| { if let Some(ref peer) = peer { trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg); } else { diff --git a/test-utils/test-runner/src/client.rs b/test-utils/test-runner/src/client.rs index 4604b4537af39..d130993bff4c7 100644 --- a/test-utils/test-runner/src/client.rs +++ b/test-utils/test-runner/src/client.rs @@ -16,44 +16,54 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Client parts -use sp_transaction_pool::runtime_api::TaggedTransactionQueue; -use sp_consensus_babe::BabeApi; -use sp_finality_grandpa::GrandpaApi; -use crate::{ChainInfo, default_config}; -use manual_seal::consensus::babe::{BabeConsensusDataProvider, SlotTimestampProvider}; -use sp_keyring::sr25519::Keyring::Alice; -use std::str::FromStr; -use sp_runtime::traits::Header; +use crate::{default_config, ChainInfo}; use futures::channel::mpsc; use jsonrpc_core::MetaIoHandler; -use manual_seal::{run_manual_seal, EngineCommand, ManualSealParams, import_queue, rpc::{ManualSeal, ManualSealApi}}; +use manual_seal::{ + consensus::babe::{BabeConsensusDataProvider, SlotTimestampProvider}, + import_queue, + rpc::{ManualSeal, ManualSealApi}, + run_manual_seal, EngineCommand, ManualSealParams, +}; use sc_client_api::backend::Backend; use sc_service::{ - build_network, spawn_tasks, BuildNetworkParams, SpawnTasksParams, TFullBackend, - TFullClient, TaskManager, new_full_parts, Configuration, ChainSpec, TaskExecutor, + build_network, new_full_parts, spawn_tasks, BuildNetworkParams, ChainSpec, Configuration, + SpawnTasksParams, TFullBackend, TFullClient, TaskExecutor, TaskManager, }; use sc_transaction_pool::BasicPool; use sc_transaction_pool_api::TransactionPool; use sp_api::{ApiExt, ConstructRuntimeApi, Core, Metadata}; use sp_block_builder::BlockBuilder; -use sp_runtime::traits::Block as BlockT; -use sp_session::SessionKeys; +use sp_consensus_babe::BabeApi; +use sp_finality_grandpa::GrandpaApi; +use sp_keyring::sr25519::Keyring::Alice; use sp_offchain::OffchainWorkerApi; -use std::sync::Arc; +use sp_runtime::traits::{Block as BlockT, Header}; +use sp_session::SessionKeys; +use sp_transaction_pool::runtime_api::TaggedTransactionQueue; +use std::{str::FromStr, sync::Arc}; type ClientParts = ( Arc>, TaskManager, - Arc::Block, ::RuntimeApi, ::Executor>>, - Arc::Block, - Hash = <::Block as BlockT>::Hash, - Error = sc_transaction_pool::error::Error, - InPoolTransaction = sc_transaction_pool::Transaction< - <::Block as BlockT>::Hash, - <::Block as BlockT>::Extrinsic, + Arc< + TFullClient< + ::Block, + ::RuntimeApi, + ::Executor, >, - >>, + >, + Arc< + dyn TransactionPool< + Block = ::Block, + Hash = <::Block as BlockT>::Hash, + Error = sc_transaction_pool::error::Error, + InPoolTransaction = sc_transaction_pool::Transaction< + <::Block as BlockT>::Hash, + <::Block as BlockT>::Extrinsic, + >, + >, + >, mpsc::Sender::Block as BlockT>::Hash>>, Arc::Block>>, ); @@ -63,27 +73,36 @@ pub enum ConfigOrChainSpec { /// Configuration object Config(Configuration), /// Chain spec object - ChainSpec(Box, TaskExecutor) + ChainSpec(Box, TaskExecutor), } /// Creates all the client parts you need for [`Node`](crate::node::Node) -pub fn client_parts(config_or_chain_spec: ConfigOrChainSpec) -> Result, sc_service::Error> - where - T: ChainInfo + 'static, - >>::RuntimeApi: - Core + Metadata + OffchainWorkerApi + SessionKeys - + TaggedTransactionQueue + BlockBuilder + BabeApi +pub fn client_parts( + config_or_chain_spec: ConfigOrChainSpec, +) -> Result, sc_service::Error> +where + T: ChainInfo + 'static, + , + >>::RuntimeApi: Core + + Metadata + + OffchainWorkerApi + + SessionKeys + + TaggedTransactionQueue + + BlockBuilder + + BabeApi + ApiExt as Backend>::State> + GrandpaApi, - ::Call: From>, - <::Block as BlockT>::Hash: FromStr, - <<::Block as BlockT>::Header as Header>::Number: num_traits::cast::AsPrimitive, + ::Call: From>, + <::Block as BlockT>::Hash: FromStr, + <<::Block as BlockT>::Header as Header>::Number: + num_traits::cast::AsPrimitive, { use sp_consensus_babe::AuthorityId; let config = match config_or_chain_spec { ConfigOrChainSpec::Config(config) => config, - ConfigOrChainSpec::ChainSpec(chain_spec, task_executor) => { - default_config(task_executor, chain_spec) - }, + ConfigOrChainSpec::ChainSpec(chain_spec, task_executor) => + default_config(task_executor, chain_spec), }; let (client, backend, keystore, mut task_manager) = @@ -92,8 +111,12 @@ pub fn client_parts(config_or_chain_spec: ConfigOrChainSpec) -> Result), select_chain.clone(), None)?; + let (grandpa_block_import, ..) = grandpa::block_import( + client.clone(), + &(client.clone() as Arc<_>), + select_chain.clone(), + None, + )?; let slot_duration = sc_consensus_babe::Config::get_or_compute(&*client)?; let (block_import, babe_link) = sc_consensus_babe::block_import( @@ -108,7 +131,7 @@ pub fn client_parts(config_or_chain_spec: ConfigOrChainSpec) -> Result(config_or_chain_spec: ConfigOrChainSpec) -> Result(config_or_chain_spec: ConfigOrChainSpec) -> Result(config_or_chain_spec: ConfigOrChainSpec) -> Result(config_or_chain_spec: ConfigOrChainSpec) -> Result Date: Thu, 22 Jul 2021 15:49:46 +0200 Subject: [PATCH 10/20] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- client/network/src/config.rs | 2 +- client/network/src/protocol/sync.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 423823d947f04..0b6241daa51fb 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -395,7 +395,7 @@ pub enum SyncMode { /// Download indexed transactions for recent blocks. storage_chain_mode: bool, }, - /// GRANDPA Warp sync - verify authority set transitions and the latest state. + /// Warp sync - verify authority set transitions and the latest state. Warp, } diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index c67a5d127d867..da8915394e752 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -513,7 +513,7 @@ pub enum SyncMode { Full, // Sync headers and the last finalied state LightState { storage_chain_mode: bool, skip_proofs: bool }, - // GRANDPA warp sync mode. + // Warp sync mode. Warp, } From 1c27a28050a247a130537254e30aa4edc7588289 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 23 Jul 2021 10:52:46 +0200 Subject: [PATCH 11/20] Fixed chage trie pruning wrt missing blocks --- client/db/src/changes_tries_storage.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/db/src/changes_tries_storage.rs b/client/db/src/changes_tries_storage.rs index 6b948a2d2c5c3..8f739cf4127a4 100644 --- a/client/db/src/changes_tries_storage.rs +++ b/client/db/src/changes_tries_storage.rs @@ -358,18 +358,23 @@ impl DbChangesTrieStorage { let next_config = match cache_tx { Some(cache_tx) if config_for_new_block && cache_tx.new_config.is_some() => { let config = cache_tx.new_config.clone().expect("guarded by is_some(); qed"); - ChangesTrieConfigurationRange { + Ok(ChangesTrieConfigurationRange { zero: (block_num, block_hash), end: None, config, - } + }) }, _ if config_for_new_block => self.configuration_at(&BlockId::Hash( *new_header .expect("config_for_new_block is only true when new_header is passed; qed") .parent_hash(), - ))?, - _ => self.configuration_at(&BlockId::Hash(next_digest_range_start_hash))?, + )), + _ => self.configuration_at(&BlockId::Hash(next_digest_range_start_hash)), + }; + let next_config = match next_config { + Ok(next_config) => next_config, + Err(ClientError::UnknownBlock(_)) => break, // No block means nothing to prune. + Err(e) => return Err(e), }; if let Some(config) = next_config.config { let mut oldest_digest_range = config From 8dfd07fd7d9ee189b08076089259ad91fe3fb70a Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 23 Jul 2021 10:56:15 +0200 Subject: [PATCH 12/20] Restore parent finalization --- client/service/src/client/client.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index e7927904765df..7d825c7b5f714 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -830,6 +830,18 @@ where None }, }; + // Ensure parent chain is finalized to maintain invariant that + // finality is called sequentially. This will also send finality + // notifications for top 250 newly finalized blocks. + if finalized && parent_exists { + self.apply_finality_with_block_hash( + operation, + parent_hash, + None, + info.best_hash, + make_notifications, + )?; + } operation.op.update_cache(new_cache); storage_changes From fc8b9fc388cb0cbe328863b6520ff6e9798f18cf Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 23 Jul 2021 10:59:35 +0200 Subject: [PATCH 13/20] fmt --- client/consensus/pow/src/lib.rs | 5 +---- client/finality-grandpa/src/import.rs | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 08d70826a69ce..879ef907281f1 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -48,10 +48,7 @@ use futures::{Future, StreamExt}; use log::*; use parking_lot::Mutex; use prometheus_endpoint::Registry; -use sc_client_api::{ - self, - {backend::AuxStore, BlockOf, BlockchainEvents}, -}; +use sc_client_api::{self, backend::AuxStore, BlockOf, BlockchainEvents}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache}; diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index bc416d4c5ea7d..570ef0b15fb73 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -777,7 +777,7 @@ where // send the command to the voter let _ = self.send_voter_commands.unbounded_send(command); }, - Err(CommandOrError::Error(e)) => { + Err(CommandOrError::Error(e)) => return Err(match e { Error::Grandpa(error) => ConsensusError::ClientImport(error.to_string()), Error::Network(error) => ConsensusError::ClientImport(error), @@ -787,8 +787,7 @@ where Error::Signing(error) => ConsensusError::ClientImport(error), Error::Timer(error) => ConsensusError::ClientImport(error.to_string()), Error::RuntimeApi(error) => ConsensusError::ClientImport(error.to_string()), - }) - }, + }), Ok(_) => { assert!( !enacts_change, From f07d173658f7ed5c4908b60dcc0755481fd5621c Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 23 Jul 2021 12:11:13 +0200 Subject: [PATCH 14/20] fmt --- client/network/src/request_responses.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index f51055af55243..226e1c546d6c9 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -788,7 +788,7 @@ pub enum ResponseFailure { /// Implements the libp2p [`RequestResponseCodec`] trait. Defines how streams of bytes are turned /// into requests and responses and vice-versa. #[derive(Debug, Clone)] -#[doc(hidden)]// Needs to be public in order to satisfy the Rust compiler. +#[doc(hidden)] // Needs to be public in order to satisfy the Rust compiler. pub struct GenericCodec { max_request_size: u64, max_response_size: u64, From 401b98dfbdb8c506706da1c8a698ca27a234b74e Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 23 Jul 2021 17:02:50 +0200 Subject: [PATCH 15/20] Revert pwasm-utils bump --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 319aca390c619..55cd9a96fd54e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6273,9 +6273,9 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +checksum = "e90f6931e6b3051e208a449c342246cb7c786ef300789b95619f46f1dd75d9b0" dependencies = [ "fixed-hash", "impl-codec", @@ -6449,9 +6449,9 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e517f47d9964362883182404b68d0b6949382c0baa40aa5ffca94f5f1e3481" +checksum = "f0c1a2f10b47d446372a4f397c58b329aaea72b2daf9395a623a411cb8ccb54f" dependencies = [ "byteorder", "log", From d95ca0ef85774959aec96deaed32f848add52614 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 29 Jul 2021 19:01:33 +0200 Subject: [PATCH 16/20] Change error type & check API version --- client/finality-grandpa/src/import.rs | 44 +++++++++++++--------- client/finality-grandpa/src/warp_proof.rs | 16 +++++--- client/network/src/warp_request_handler.rs | 10 +++-- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 570ef0b15fb73..7474039021675 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -24,7 +24,7 @@ use parity_scale_codec::{Decode, Encode}; use sc_client_api::{backend::Backend, utils::is_descendent_of}; use sc_consensus::shared_data::{SharedDataLocked, SharedDataLockedUpgradable}; use sc_telemetry::TelemetryHandle; -use sp_api::TransactionFor; +use sp_api::{Core, RuntimeApiInfo, TransactionFor}; use sp_blockchain::{well_known_cache_keys, BlockStatus}; use sp_consensus::{ BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, Error as ConsensusError, @@ -426,24 +426,34 @@ where /// Read current set id form a given state. fn current_set_id(&self, id: &BlockId) -> Result { - match self.inner.runtime_api().current_set_id(&id) { - Ok(set_id) => Ok(set_id), - Err(sp_api::ApiError::Application(_)) => { - // The new API is not supported in this runtime. Try reading directly from storage. - // This code may be removed once warp sync to an old runtime is no longer needed. - for prefix in ["GrandpaFinality", "Grandpa"] { - let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat(); - if let Ok(Some(id)) = - self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec())) - { - if let Ok(id) = SetId::decode(&mut id.0.as_ref()) { - return Ok(id) - } + let runtime_version = self.inner.runtime_api().version(id).map_err(|e| { + ConsensusError::ClientImport(format!( + "Unable to retrieve current runtime version. {}", + e + )) + })?; + if runtime_version + .api_version(&>::ID) + .map_or(false, |v| v < 3) + { + // The new API is not supported in this runtime. Try reading directly from storage. + // This code may be removed once warp sync to an old runtime is no longer needed. + for prefix in ["GrandpaFinality", "Grandpa"] { + let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat(); + if let Ok(Some(id)) = + self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec())) + { + if let Ok(id) = SetId::decode(&mut id.0.as_ref()) { + return Ok(id) } } - Err(ConsensusError::ClientImport("Unable to read retrieve current set id.".into())) - }, - Err(e) => Err(ConsensusError::ClientImport(e.to_string())), + } + Err(ConsensusError::ClientImport("Unable to retrieve current set id.".into())) + } else { + self.inner + .runtime_api() + .current_set_id(&id) + .map_err(|e| ConsensusError::ClientImport(e.to_string())) } } diff --git a/client/finality-grandpa/src/warp_proof.rs b/client/finality-grandpa/src/warp_proof.rs index ebb437a479722..86b57c78a43e5 100644 --- a/client/finality-grandpa/src/warp_proof.rs +++ b/client/finality-grandpa/src/warp_proof.rs @@ -52,6 +52,8 @@ pub enum Error { MissingData, } +impl std::error::Error for Error {} + /// The maximum size in bytes of the `WarpSyncProof`. pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 8 * 1024 * 1024; @@ -253,13 +255,16 @@ impl> WarpSyncProvider where NumberFor: BlockNumberOps, { - fn generate(&self, start: Block::Hash) -> Result { + fn generate( + &self, + start: Block::Hash, + ) -> Result> { let proof = WarpSyncProof::::generate( &*self.backend, start, &self.authority_set.authority_set_changes(), ) - .map_err(|e| e.to_string())?; + .map_err(Box::new)?; Ok(EncodedProof(proof.encode())) } @@ -268,7 +273,7 @@ where proof: &EncodedProof, set_id: SetId, authorities: AuthorityList, - ) -> Result, String> { + ) -> Result, Box> { let EncodedProof(proof) = proof; let proof = WarpSyncProof::::decode(&mut proof.as_slice()) .map_err(|e| format!("Proof decoding error: {:?}", e))?; @@ -277,9 +282,8 @@ where .last() .map(|p| p.header.clone()) .ok_or_else(|| "Empty proof".to_string())?; - let (next_set_id, next_authorities) = proof - .verify(set_id, authorities) - .map_err(|e| format!("Proof verification error: {:?}", e))?; + let (next_set_id, next_authorities) = + proof.verify(set_id, authorities).map_err(Box::new)?; if proof.is_finished { Ok(VerificationResult::::Complete(next_set_id, next_authorities, last_header)) } else { diff --git a/client/network/src/warp_request_handler.rs b/client/network/src/warp_request_handler.rs index 31a6a2d4f60cb..beb9d1ce528a8 100644 --- a/client/network/src/warp_request_handler.rs +++ b/client/network/src/warp_request_handler.rs @@ -50,14 +50,17 @@ pub enum VerificationResult { /// Warp sync backend. Handles retrieveing and verifying warp sync proofs. pub trait WarpSyncProvider: Send + Sync { /// Generate proof starting at given block hash. The proof is accumulated until maximum proof size is reached. - fn generate(&self, start: B::Hash) -> Result; + fn generate( + &self, + start: B::Hash, + ) -> Result>; /// Verify warp proof agains current set of authorities. fn verify( &self, proof: &EncodedProof, set_id: SetId, authorities: AuthorityList, - ) -> Result, String>; + ) -> Result, Box>; /// Get current list of authorities. This is supposed to be genesis authorities when starting sync. fn current_authorities(&self) -> AuthorityList; } @@ -151,7 +154,8 @@ enum HandleRequestError { DecodeScale(codec::Error), Client(sp_blockchain::Error), #[from(ignore)] - InvalidRequest(String), + #[display(fmt = "Invalid request {}.", _0)] + InvalidRequest(Box), #[display(fmt = "Failed to send response.")] SendResponse, } From f2c9dd78d7f85388909a5b46fe31564612336767 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 30 Jul 2021 07:26:25 +0200 Subject: [PATCH 17/20] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- client/consensus/babe/src/lib.rs | 5 +---- client/db/src/lib.rs | 16 +++++++--------- client/finality-grandpa/src/import.rs | 5 +---- client/network/README.md | 2 +- client/network/src/protocol/sync.rs | 2 +- client/network/src/protocol/sync/warp.rs | 2 +- client/service/src/builder.rs | 5 +---- primitives/consensus/common/src/import_queue.rs | 5 +---- 8 files changed, 14 insertions(+), 28 deletions(-) diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 0ada3ee4f0f20..b517c81a10748 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1420,10 +1420,7 @@ where Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } - if matches!( - block.state_action, - StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_)) - ) { + if block.with_state() { return self.import_state(block, new_cache).await } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 2587a64df6046..2f228b88e2d74 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1267,15 +1267,13 @@ impl Backend { ) -> ClientResult<()> { let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); - if last_finalized != self.blockchain.meta.read().genesis_hash { - if *header.parent_hash() != last_finalized { - return Err(::sp_blockchain::Error::NonSequentialFinalization(format!( - "Last finalized {:?} not parent of {:?}", - last_finalized, - header.hash() - )) - .into()) - } + if last_finalized != self.blockchain.meta.read().genesis_hash && *header.parent_hash() != last_finalized { + return Err(sp_blockchain::Error::NonSequentialFinalization(format!( + "Last finalized {:?} not parent of {:?}", + last_finalized, + header.hash() + )) + .into()) } Ok(()) } diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 570ef0b15fb73..c763bda0f8e61 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -535,10 +535,7 @@ where Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } - if matches!( - block.state_action, - StateAction::ApplyChanges(sp_consensus::StorageChanges::Import(_)) - ) { + if block.with_state() { return self.import_state(block, new_cache).await } diff --git a/client/network/README.md b/client/network/README.md index 9522bd26b1c35..c361bc9249f71 100644 --- a/client/network/README.md +++ b/client/network/README.md @@ -229,7 +229,7 @@ All data that's announced. In this mode the initial downloads and verifies full header history. This allows to validate authority set transitions and arrive at a recent header. After header chain is verified and imported -the node starts downloading a state snapshot using the state request protocol. Each `StateRequest` +the node starts downloading a state snapshot using the state request protocol. Each `StateRequest` contains a starting storage key, which is empty for the first request. `StateResponse` contains a storage proof for a sequence of keys and values in the storage starting (but not including) from the key that is in the request. After iterating the proof trie against diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 5b4e17dc0e076..a741fdc0cbb18 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -1269,7 +1269,7 @@ impl ChainSync { /// Handle a response from the remote to a warp proof request that we made. /// - /// Returns next request if any. + /// Returns next request. pub fn on_warp_sync_data( &mut self, who: &PeerId, diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 51a49c3764bae..6768b22f0b423 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. +// Copyright (C) 2021 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 diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 603e24ad71997..cf0c7c2419b8d 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -355,10 +355,7 @@ where wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), no_genesis: matches!( config.network.sync_mode, - sc_network::config::SyncMode::Fast { .. } - ) || matches!( - config.network.sync_mode, - sc_network::config::SyncMode::Warp + sc_network::config::SyncMode::Fast { .. } | sc_network::config::SyncMode::Warp ), wasm_runtime_substitutes, }, diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index cce5aabcf1b6c..435eafa5b4220 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -294,10 +294,7 @@ pub(crate) async fn import_single_block_metered< metrics.report_verification(true, started.elapsed()); } - let mut cache = HashMap::new(); - if let Some(keys) = maybe_keys { - cache.extend(keys.into_iter()); - } + let cache = HashMap::from_iter(keys.unwrap_or_default()); let import_block = import_block.clear_storage_changes_and_mutate(); let imported = import_handle.import_block(import_block, cache).await; if let Some(metrics) = metrics.as_ref() { From bf1277972d93a83b4e429db570d0f0ec9368def5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 30 Jul 2021 07:30:03 +0200 Subject: [PATCH 18/20] Build fix --- client/db/src/lib.rs | 4 +++- client/finality-grandpa/src/import.rs | 2 +- primitives/consensus/common/src/import_queue.rs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 2f228b88e2d74..dda469f4fd336 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1267,7 +1267,9 @@ impl Backend { ) -> ClientResult<()> { let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); - if last_finalized != self.blockchain.meta.read().genesis_hash && *header.parent_hash() != last_finalized { + if last_finalized != self.blockchain.meta.read().genesis_hash && + *header.parent_hash() != last_finalized + { return Err(sp_blockchain::Error::NonSequentialFinalization(format!( "Last finalized {:?} not parent of {:?}", last_finalized, diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index f342d0cfe8d94..d4a0be0987e36 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -28,7 +28,7 @@ use sp_api::{Core, RuntimeApiInfo, TransactionFor}; use sp_blockchain::{well_known_cache_keys, BlockStatus}; use sp_consensus::{ BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, Error as ConsensusError, - ImportResult, JustificationImport, SelectChain, StateAction, + ImportResult, JustificationImport, SelectChain, }; use sp_core::hashing::twox_128; use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index 435eafa5b4220..567bb6c1cce26 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -26,7 +26,7 @@ //! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial //! queues to be instantiated simply. -use std::collections::HashMap; +use std::{collections::HashMap, iter::FromIterator}; use sp_runtime::{ traits::{Block as BlockT, Header as _, NumberFor}, @@ -294,7 +294,7 @@ pub(crate) async fn import_single_block_metered< metrics.report_verification(true, started.elapsed()); } - let cache = HashMap::from_iter(keys.unwrap_or_default()); + let cache = HashMap::from_iter(maybe_keys.unwrap_or_default()); let import_block = import_block.clear_storage_changes_and_mutate(); let imported = import_handle.import_block(import_block, cache).await; if let Some(metrics) = metrics.as_ref() { From 9aea448927ecce3f8d4d45824394d1bcc3db3894 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 30 Jul 2021 08:13:39 +0200 Subject: [PATCH 19/20] Fixed target block check --- client/finality-grandpa/src/import.rs | 5 ++-- client/network/src/protocol/sync.rs | 35 +++++++++++++++-------- client/network/src/protocol/sync/warp.rs | 36 ++++++++++-------------- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index d4a0be0987e36..f4a577c33592b 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -470,8 +470,9 @@ where let import_result = (&*self.inner).import_block(block, new_cache).await; match import_result { Ok(ImportResult::Imported(aux)) => { - // We've just imported a new state. We trust the sync module that the state - // is correct and final. So we can read the authority list and set id from the state. + // We've just imported a new state. We trust the sync module has verified + // finality proofs and that the state is correct and final. + // So we can read the authority list and set id from the state. self.authority_set_hard_forks.clear(); let block_id = BlockId::hash(hash); let authorities = self diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index a741fdc0cbb18..51ee704283ac1 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -104,6 +104,9 @@ const STATE_SYNC_FINALITY_THRESHOLD: u32 = 8; /// so far behind. const MAJOR_SYNC_BLOCKS: u8 = 5; +/// Number of peers that need to be connected before warp sync is started. +const MIN_PEERS_TO_START_WARP_SYNC: usize = 3; + mod rep { use sc_peerset::ReputationChange as Rep; /// Reputation change when a peer sent us a message that led to a @@ -688,8 +691,8 @@ impl ChainSync { } if let SyncMode::Warp = &self.mode { - // TODO: wait for 3 peers - if self.warp_sync.is_none() { + if self.peers.len() >= MIN_PEERS_TO_START_WARP_SYNC && self.warp_sync.is_none() + { log::debug!(target: "sync", "Starting warp state sync."); if let Some(provider) = &self.warp_sync_provider { self.warp_sync = @@ -978,9 +981,11 @@ impl ChainSync { if sync.is_complete() { return None } - if let Some(request) = sync.next_state_request() { + if let (Some(request), Some(target)) = + (sync.next_state_request(), sync.target_block_number()) + { for (id, peer) in self.peers.iter_mut() { - if peer.state.is_available() && peer.best_number >= sync.target_block_number() { + if peer.state.is_available() && peer.best_number >= target { trace!(target: "sync", "New StateRequest for {}", id); peer.state = PeerSyncState::DownloadingState; return Some((id.clone(), request)) @@ -1006,11 +1011,17 @@ impl ChainSync { return None } if let Some(request) = sync.next_warp_poof_request() { - for (id, peer) in self.peers.iter_mut() { - if peer.state.is_available() && peer.best_number >= sync.target_block_number() { - trace!(target: "sync", "New WarpProofRequest for {}", id); - peer.state = PeerSyncState::DownloadingWarpProof; - return Some((id.clone(), request)) + let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect(); + if !targets.is_empty() { + targets.sort(); + let median = targets[targets.len() / 2]; + // Find a random peer that is synced as much as peer majority. + for (id, peer) in self.peers.iter_mut() { + if peer.state.is_available() && peer.best_number >= median { + trace!(target: "sync", "New WarpProofRequest for {}", id); + peer.state = PeerSyncState::DownloadingWarpProof; + return Some((id.clone(), request)) + } } } } @@ -1466,8 +1477,10 @@ impl ChainSync { self.mode = SyncMode::Full; output.extend(self.restart()); } - let warp_sync_complete = - self.warp_sync.as_ref().map_or(false, |s| s.target_block_hash() == hash); + let warp_sync_complete = self + .warp_sync + .as_ref() + .map_or(false, |s| s.target_block_hash() == Some(hash)); if warp_sync_complete { info!( target: "sync", diff --git a/client/network/src/protocol/sync/warp.rs b/client/network/src/protocol/sync/warp.rs index 6768b22f0b423..fae0e2f5452a7 100644 --- a/client/network/src/protocol/sync/warp.rs +++ b/client/network/src/protocol/sync/warp.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +///! Warp sync support. pub use super::state::ImportResult; use super::state::StateSync; pub use crate::warp_request_handler::{ @@ -27,11 +28,9 @@ use crate::{ WarpSyncPhase, WarpSyncProgress, }; use sp_finality_grandpa::{AuthorityList, SetId}; -use sp_runtime::traits::{Block as BlockT, Header, NumberFor, Zero}; +use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; use std::sync::Arc; -/// Warp sync support. - enum Phase { WarpProof { set_id: SetId, authorities: AuthorityList, last_hash: B::Hash }, State(StateSync), @@ -49,8 +48,6 @@ pub enum WarpProofImportResult { /// Warp sync state machine. Accumulates warp proofs and state. pub struct WarpSync { - target_hash: B::Hash, - target_num: NumberFor, phase: Phase, client: Arc>, warp_sync_provider: Arc>, @@ -69,14 +66,7 @@ impl WarpSync { authorities: warp_sync_provider.current_authorities(), last_hash, }; - WarpSync { - client, - warp_sync_provider, - target_hash: Default::default(), - target_num: Zero::zero(), - phase, - total_proof_bytes: 0, - } + WarpSync { client, warp_sync_provider, phase, total_proof_bytes: 0 } } /// Validate and import a state reponse. @@ -119,8 +109,6 @@ impl WarpSync { }, Ok(VerificationResult::Complete(new_set_id, _, header)) => { log::debug!(target: "sync", "Verified complete proof, set_id={:?}", new_set_id); - self.target_hash = header.hash(); - self.target_num = *header.number(); self.total_proof_bytes += response.0.len() as u64; let state_sync = StateSync::new(self.client.clone(), header, false); let request = state_sync.next_request(); @@ -149,14 +137,20 @@ impl WarpSync { } } - /// Return target block hash. - pub fn target_block_hash(&self) -> B::Hash { - self.target_hash.clone() + /// Return target block hash if it is known. + pub fn target_block_hash(&self) -> Option { + match &self.phase { + Phase::State(s) => Some(s.target()), + Phase::WarpProof { .. } => None, + } } - /// Return target block number. - pub fn target_block_number(&self) -> NumberFor { - self.target_num + /// Return target block number if it is known. + pub fn target_block_number(&self) -> Option> { + match &self.phase { + Phase::State(s) => Some(s.target_block_num()), + Phase::WarpProof { .. } => None, + } } /// Check if the state is complete. From 78de046369b0d70bd154c751865d2d08d2df2d72 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 30 Jul 2021 18:23:02 +0200 Subject: [PATCH 20/20] Formatting --- client/consensus/pow/src/lib.rs | 3 +-- client/finality-grandpa/src/import.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index ef4e9ee58d162..17bd02f6a5651 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -58,8 +58,7 @@ use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache}; use sp_consensus::{ - CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain, - SyncOracle, + CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle, }; use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index dc23c78f58117..a86421b4a0ef0 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -26,10 +26,10 @@ use sc_consensus::{ shared_data::{SharedDataLocked, SharedDataLockedUpgradable}, BlockCheckParams, BlockImport, BlockImportParams, ImportResult, JustificationImport, }; -use sp_consensus::{Error as ConsensusError, SelectChain, BlockOrigin}; use sc_telemetry::TelemetryHandle; use sp_api::{Core, RuntimeApiInfo, TransactionFor}; use sp_blockchain::{well_known_cache_keys, BlockStatus}; +use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; use sp_core::hashing::twox_128; use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{