diff --git a/Cargo.lock b/Cargo.lock index b10e2074167a2..69a338089c6b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7201,6 +7201,7 @@ dependencies = [ "sc-chain-spec", "sc-client-api", "sc-client-db", + "sc-consensus-babe", "sc-executor", "sc-finality-grandpa", "sc-informant", diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 47fec977f5e82..676fd3a65816f 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -200,6 +200,9 @@ pub trait BlockImportOperation { /// Mark a block as new head. If both block import and set head are specified, set head /// overrides block import's best block rule. fn mark_head(&mut self, id: BlockId) -> sp_blockchain::Result<()>; + + /// Can the block still be imported if the parent block is not found. + fn allow_missing_parent(&mut self, allow: bool); } /// Interface for performing operations on the backend. diff --git a/client/api/src/cht.rs b/client/api/src/cht.rs index 30cfd3a1b671b..1829fc3f496a7 100644 --- a/client/api/src/cht.rs +++ b/client/api/src/cht.rs @@ -267,9 +267,7 @@ fn build_pairs( let mut pairs = Vec::new(); let mut hash_index = Header::Number::zero(); for hash in hashes.into_iter() { - let hash = hash?.ok_or_else(|| ClientError::from( - ClientError::MissingHashRequiredForCHT - ))?; + let hash = hash?.unwrap_or_else(|| panic!("MissingHashRequiredForCHT")); pairs.push(( encode_cht_key(start_num + hash_index).to_vec(), encode_cht_value(hash) @@ -283,7 +281,7 @@ fn build_pairs( if hash_index == cht_size { Ok(pairs) } else { - Err(ClientError::MissingHashRequiredForCHT) + panic!("MissingHashRequiredForCHT") } } diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index ded030fb8046f..cb573706d314d 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -167,10 +167,11 @@ impl Blockchain { justification: Option, body: Option::Extrinsic>>, new_state: NewBlockState, + allow_missing_parent: bool, ) -> sp_blockchain::Result<()> { let number = header.number().clone(); if new_state.is_best() { - self.apply_head(&header)?; + self.apply_head(&header, allow_missing_parent)?; } { @@ -231,10 +232,10 @@ impl Blockchain { None => return Err(sp_blockchain::Error::UnknownBlock(format!("{}", id))), }; - self.apply_head(&header) + self.apply_head(&header, false) } - fn apply_head(&self, header: &::Header) -> sp_blockchain::Result<()> { + fn apply_head(&self, header: &::Header, allow_missing_parent: bool) -> sp_blockchain::Result<()> { let hash = header.hash(); let number = header.number(); @@ -242,7 +243,7 @@ impl Blockchain { // write lock. let best_tree_route = { let best_hash = self.storage.read().best_hash; - if &best_hash == header.parent_hash() { + if &best_hash == header.parent_hash() || allow_missing_parent { None } else { let route = sp_blockchain::tree_route(self, best_hash, *header.parent_hash())?; @@ -427,9 +428,10 @@ impl light::Storage for Blockchain _cache: HashMap>, state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, + allow_missing_parent: bool, ) -> sp_blockchain::Result<()> { let hash = header.hash(); - self.insert(hash, header, None, None, state)?; + self.insert(hash, header, None, None, state, allow_missing_parent)?; self.write_aux(aux_ops); Ok(()) @@ -487,6 +489,7 @@ pub struct BlockImportOperation { aux: Vec<(Vec, Option>)>, finalized_blocks: Vec<(BlockId, Option)>, set_head: Option>, + allow_missing_parent: bool, } impl backend::BlockImportOperation for BlockImportOperation where @@ -581,6 +584,10 @@ impl backend::BlockImportOperation for BlockImportOperatio self.set_head = Some(block); Ok(()) } + + fn allow_missing_parent(&mut self, allow: bool) { + self.allow_missing_parent = allow; + } } /// In-memory backend. Keeps all states and blocks in memory. @@ -636,6 +643,7 @@ impl backend::Backend for Backend where Block::Hash aux: Default::default(), finalized_blocks: Default::default(), set_head: None, + allow_missing_parent: false, }) } @@ -671,7 +679,10 @@ impl backend::Backend for Backend where Block::Hash self.states.write().insert(hash, new_state); - self.blockchain.insert(hash, header, justification, body, pending_block.state)?; + self.blockchain.insert( + hash, header, justification, body, pending_block.state, + operation.allow_missing_parent, + )?; } if !operation.aux.is_empty() { diff --git a/client/api/src/light.rs b/client/api/src/light.rs index 144851dac0075..3c71e2c7676ab 100644 --- a/client/api/src/light.rs +++ b/client/api/src/light.rs @@ -245,6 +245,7 @@ pub trait Storage: AuxStore + HeaderBackend cache: HashMap>, state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, + allow_missing_parent: bool, ) -> ClientResult<()>; /// Set an existing block as new best block. diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 39c47e32908df..34cd953fa657c 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -266,6 +266,10 @@ impl ChainSpec { fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) { self.client_spec.light_sync_state = Some(light_sync_state); } + + fn get_light_sync_state(&self) -> Option<&SerializableLightSyncState> { + self.client_spec.light_sync_state.as_ref() + } } impl ChainSpec { @@ -395,6 +399,10 @@ where fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) { ChainSpec::set_light_sync_state(self, light_sync_state) } + + fn get_light_sync_state(&self) -> Option<&SerializableLightSyncState> { + ChainSpec::get_light_sync_state(self) + } } /// Hardcoded infomation that allows light clients to sync quickly. diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index 94ed93758bb2d..682782de7ada8 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -159,6 +159,8 @@ pub trait ChainSpec: BuildStorage + Send + Sync { fn set_storage(&mut self, storage: Storage); /// Hardcode infomation to allow light clients to sync quickly into the chain spec. fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState); + /// Get the light sync state, if available. + fn get_light_sync_state(&self) -> Option<&SerializableLightSyncState>; } impl std::fmt::Debug for dyn ChainSpec { diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 287121566a417..8ae13389642f4 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -110,7 +110,7 @@ pub(crate) fn write_epoch_changes( } /// Write the cumulative chain-weight of a block ot aux storage. -pub(crate) fn write_block_weight( +pub fn write_block_weight( block_hash: H, block_weight: BabeBlockWeight, write_aux: F, diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 6105e9876bb5e..2baf711a53cc1 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1275,10 +1275,12 @@ impl BlockImport for BabeBlockImport( // 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.lock(), )?; + */ let import = BabeBlockImport::new( client, diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index acb07dd668a3c..db0f829f8aeb9 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -76,7 +76,7 @@ pub trait Epoch { /// Descriptor for the next epoch. type NextEpochDescriptor; /// Type of the slot number. - type SlotNumber: Ord + Copy; + type SlotNumber: Ord + Copy + std::fmt::Debug; /// The starting slot of the epoch. fn start_slot(&self) -> Self::SlotNumber; @@ -249,7 +249,7 @@ impl<'a, E: Epoch> From<&'a PersistedEpoch> for PersistedEpochHeader { } /// Persisted epoch header stored in ForkTree. -#[derive(Encode, Decode, PartialEq, Eq)] +#[derive(Debug, Encode, Decode, PartialEq, Eq)] pub enum PersistedEpochHeader { /// Genesis persisted epoch header. epoch_0, epoch_1. Genesis(EpochHeader, EpochHeader), @@ -329,6 +329,16 @@ impl EpochChanges where Self::default() } + pub fn filter(&mut self, number: Number) { + let epochs = std::mem::replace(&mut self.epochs, BTreeMap::new()); + + for (k, v) in epochs.into_iter().filter(|((_, n), _)| *n <= number) { + self.epochs.insert(k, v); + } + + self.inner = self.inner.filter(number); + } + /// Rebalances the tree of epoch changes so that it is sorted by length of /// fork (longest fork first). pub fn rebalance(&mut self) { diff --git a/client/db/src/cache/list_cache.rs b/client/db/src/cache/list_cache.rs index 15ad339b1f2c1..6fe3b55beaf46 100644 --- a/client/db/src/cache/list_cache.rs +++ b/client/db/src/cache/list_cache.rs @@ -294,6 +294,7 @@ impl> ListCache ) -> ClientResult>> { // this guarantee is currently provided by LightStorage && we're relying on it here let prev_operation = operations.operations.last(); + /* debug_assert!( entry_type != EntryType::Final || self.best_finalized_block.hash == parent.hash || @@ -303,6 +304,7 @@ impl> ListCache _ => false, } ); + */ // we do not store any values behind finalized if block.number != Zero::zero() && self.best_finalized_block.number >= block.number { diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 8196a750557a8..03fdfd76afdbc 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -734,6 +734,8 @@ impl sc_client_api::backend::BlockImportOperation for Bloc self.set_head = Some(block); Ok(()) } + + fn allow_missing_parent(&mut self, _allow: bool) {} } struct StorageDb { diff --git a/client/db/src/light.rs b/client/db/src/light.rs index acfb6217ce9e0..fada3fca8bf30 100644 --- a/client/db/src/light.rs +++ b/client/db/src/light.rs @@ -200,7 +200,7 @@ impl HeaderMetadata for LightStorage { header_metadata.clone(), ); header_metadata - }).ok_or_else(|| ClientError::UnknownBlock(format!("header not found in db: {}", hash))) + }).ok_or_else(|| panic!("header not found in db: {:?}. info: {:?}", hash, self.info())) }, Ok) } @@ -234,12 +234,13 @@ impl LightStorage { transaction: &mut Transaction, route_to: Block::Hash, best_to: (NumberFor, Block::Hash), + allow_missing_parent: bool, ) -> ClientResult<()> { let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; // handle reorg. let meta = self.meta.read(); - if meta.best_hash != Default::default() { + if meta.best_hash != Default::default() && !allow_missing_parent { let tree_route = sp_blockchain::tree_route(self, meta.best_hash, route_to)?; // update block number to hash lookup entries. @@ -284,9 +285,10 @@ impl LightStorage { transaction: &mut Transaction, header: &Block::Header, hash: Block::Hash, + allow_missing_parent: bool, ) -> ClientResult<()> { let meta = self.meta.read(); - if &meta.finalized_hash != header.parent_hash() { + if &meta.finalized_hash != header.parent_hash() && !allow_missing_parent { return Err(::sp_blockchain::Error::NonSequentialFinalization( format!("Last finalized {:?} not parent of {:?}", meta.finalized_hash, hash), @@ -296,6 +298,7 @@ impl LightStorage { let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash)?; transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key); + /* // build new CHT(s) if required if let Some(new_cht_number) = cht::is_build_required(cht::size(), *header.number()) { let new_cht_start: NumberFor = cht::start_number(cht::size(), new_cht_number); @@ -356,6 +359,7 @@ impl LightStorage { prune_block += One::one(); } } + */ Ok(()) } @@ -421,6 +425,7 @@ impl Storage for LightStorage mut cache_at: HashMap>, leaf_state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, + allow_missing_parent: bool, ) -> ClientResult<()> { let mut transaction = Transaction::new(); @@ -439,7 +444,9 @@ impl Storage for LightStorage let lookup_key = utils::number_and_hash_to_lookup_key(number, &hash)?; if leaf_state.is_best() { - self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))?; + self.set_head_with_transaction( + &mut transaction, parent_hash, (number, hash), allow_missing_parent, + )?; } utils::insert_hash_to_key_mapping( @@ -473,6 +480,7 @@ impl Storage for LightStorage &mut transaction, &header, hash, + allow_missing_parent, )?; } @@ -513,7 +521,9 @@ impl Storage for LightStorage let number = header.number(); let mut transaction = Transaction::new(); - self.set_head_with_transaction(&mut transaction, hash.clone(), (number.clone(), hash.clone()))?; + self.set_head_with_transaction( + &mut transaction, hash.clone(), (number.clone(), hash.clone()), false, + )?; self.db.commit(transaction)?; self.update_meta(hash, header.number().clone(), true, false); @@ -528,7 +538,7 @@ impl Storage for LightStorage let mut transaction = Transaction::new(); let hash = header.hash(); let number = *header.number(); - self.note_finalized(&mut transaction, &header, hash.clone())?; + self.note_finalized(&mut transaction, &header, hash.clone(), false)?; { let mut cache = self.cache.0.write(); let cache_ops = cache.transaction(&mut transaction) diff --git a/client/finality-grandpa/src/authorities.rs b/client/finality-grandpa/src/authorities.rs index 57c30bc3b25c9..1a240d8b769c9 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/finality-grandpa/src/authorities.rs @@ -108,9 +108,9 @@ pub(crate) struct Status { #[derive(Debug, Clone, Encode, Decode, PartialEq)] pub struct AuthoritySet { /// The current active authorities. - pub(crate) current_authorities: AuthorityList, + pub current_authorities: AuthorityList, /// The current set id. - pub(crate) set_id: u64, + pub set_id: u64, /// Tree of pending standard changes across forks. Standard changes are /// enacted on finality and must be enacted (i.e. finalized) in-order across /// a given branch diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 8f351b66ecf52..94815598fdec5 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -129,7 +129,7 @@ pub use finality_proof::{FinalityProofFragment, FinalityProofProvider, StorageAn pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream}; pub use import::GrandpaBlockImport; pub use justification::GrandpaJustification; -pub use light_import::{light_block_import, GrandpaLightBlockImport}; +pub use light_import::{light_block_import, GrandpaLightBlockImport, LightAuthoritySet}; pub use voting_rule::{ BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder }; diff --git a/client/finality-grandpa/src/light_import.rs b/client/finality-grandpa/src/light_import.rs index a7c9a655467c7..eaeb3235bd578 100644 --- a/client/finality-grandpa/src/light_import.rs +++ b/client/finality-grandpa/src/light_import.rs @@ -103,9 +103,9 @@ struct LightImportData { /// Latest authority set tracker. #[derive(Debug, Encode, Decode)] -struct LightAuthoritySet { - set_id: u64, - authorities: AuthorityList, +pub struct LightAuthoritySet { + pub set_id: u64, + pub authorities: AuthorityList, } impl GrandpaLightBlockImport { diff --git a/client/light/src/backend.rs b/client/light/src/backend.rs index be7953e528bd8..7810dfcbe1baf 100644 --- a/client/light/src/backend.rs +++ b/client/light/src/backend.rs @@ -70,6 +70,7 @@ pub struct ImportOperation { storage_update: Option>>, changes_trie_config_update: Option>, _phantom: std::marker::PhantomData, + allow_missing_parent: bool, } /// Either in-memory genesis state, or locally-unavailable state. @@ -135,6 +136,7 @@ impl ClientBackend for Backend> storage_update: None, changes_trie_config_update: None, _phantom: Default::default(), + allow_missing_parent: false, }) } @@ -166,6 +168,7 @@ impl ClientBackend for Backend> operation.cache, operation.leaf_state, operation.aux_ops, + operation.allow_missing_parent, )?; // when importing genesis block => remember its state @@ -366,6 +369,10 @@ impl BlockImportOperation for ImportOperation self.set_head = Some(block); Ok(()) } + + fn allow_missing_parent(&mut self, allow: bool) { + self.allow_missing_parent = allow; + } } impl std::fmt::Debug for GenesisOrUnavailableState { diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index 9c0ddbc54c904..0920fbd23a63c 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -78,6 +78,8 @@ sc-tracing = { version = "2.0.0", path = "../tracing" } sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } tracing = "0.1.19" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } +grandpa = { version = "0.8.0", package = "sc-finality-grandpa", path = "../finality-grandpa" } +sc-consensus-babe = { version = "0.8.0", path = "../consensus/babe" } [target.'cfg(not(target_os = "unknown"))'.dependencies] tempfile = "3.1.0" diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 0a8111710b30b..6726dc4e34509 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -25,13 +25,13 @@ use crate::{ config::{Configuration, KeystoreConfig, PrometheusConfig}, }; use sc_client_api::{ - light::RemoteBlockchain, ForkBlocks, BadBlocks, UsageProvider, ExecutorProvider, + light::RemoteBlockchain, ForkBlocks, BadBlocks, UsageProvider, ExecutorProvider, AuxStore, }; use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; use sc_chain_spec::get_extension; use sp_consensus::{ block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator, Chain}, - import_queue::ImportQueue, + import_queue::ImportQueue, BlockImport, }; use jsonrpc_pubsub::manager::SubscriptionManager; use futures::{ @@ -45,10 +45,11 @@ use sc_network::config::{Role, FinalityProofProvider, OnDemand, BoxFinalityProof use sc_network::NetworkService; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ - Block as BlockT, SaturatedConversion, HashFor, Zero, BlockIdTo, + Block as BlockT, Header as HeaderT, SaturatedConversion, HashFor, Zero, BlockIdTo, NumberFor, }; use sp_api::{ProvideRuntimeApi, CallApiAt}; use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo}; +use std::collections::HashMap; use std::sync::Arc; use wasm_timer::SystemTime; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; @@ -323,6 +324,11 @@ pub fn new_light_parts( ) -> Result, Error> where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, + TRtApi: sp_api::ConstructRuntimeApi>, + >>::RuntimeApi: + sp_api::Core + + sp_api::ApiErrorExt + + sp_api::ApiExt as sc_client_api::Backend>::State>, { let keystore_container = KeystoreContainer::new(&config.keystore)?; let task_manager = { @@ -356,7 +362,7 @@ pub fn new_light_parts( ); 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( + let mut client = Arc::new(light::new_light( backend.clone(), config.chain_spec.as_storage_builder(), executor, @@ -364,6 +370,62 @@ pub fn new_light_parts( config.prometheus_config.as_ref().map(|config| config.registry.clone()), )?); + use codec::Encode; + + if client.chain_info().best_number == NumberFor::::zero() { + if let Some(sync_state) = config.chain_spec.get_light_sync_state() { + let mut sync_state = sc_chain_spec::LightSyncState::::from_serializable(sync_state) + .map_err(|err| err.to_string())?; + + let las = grandpa::LightAuthoritySet { + set_id: sync_state.grandpa_authority_set.set_id, + authorities: sync_state.grandpa_authority_set.current_authorities, + }; + + println!("light state"); + println!("============"); + println!("finalized block: #{} {:?}", sync_state.finalized_block_header.number(), sync_state.finalized_block_header.hash()); + println!("babe epoch changes: {:?}", sync_state.babe_epoch_changes.tree().iter().collect::>()); + sync_state.babe_epoch_changes.filter(sync_state.finalized_block_header.number().clone()); + println!("pruned babe epoch changes: {:?}", sync_state.babe_epoch_changes.tree().iter().collect::>()); + + client.insert_aux( + &[ + (&b"babe_epoch_changes_version"[..], &(2_u32).encode()[..]), + (&b"babe_epoch_changes"[..], &sync_state.babe_epoch_changes.encode()[..]), + (&b"grandpa_voters"[..], &las.encode()[..]), + ], + &[] + )?; + + sc_consensus_babe::aux_schema::write_block_weight( + sync_state.finalized_block_header.hash(), + sync_state.babe_finalized_block_weight, + |aux| { + let mut v = Vec::new(); + for (key, value) in aux.iter() { + v.push((&key[..], *value)); + } + client.insert_aux(&v, &[]) + } + )?; + + log::info!( + "Importing block {:?} ({:?})", + sync_state.finalized_block_header.hash(), sync_state.finalized_block_header.number() + ); + let origin = sp_consensus::BlockOrigin::File; + let mut block_import_params = sp_consensus::BlockImportParams::new( + origin, sync_state.finalized_block_header, + ); + block_import_params.allow_missing_parent = true; + block_import_params.finalized = true; + block_import_params.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain); + let res = client.import_block(block_import_params, HashMap::new())?; + assert!(matches!(res, sp_consensus::ImportResult::Imported(_))); + } + } + Ok((client, backend, keystore_container, task_manager, on_demand)) } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 64b00f81905dc..4de27941730fc 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -615,6 +615,7 @@ impl Client where fork_choice, intermediates, import_existing, + allow_missing_parent, .. } = import_block; @@ -654,6 +655,7 @@ impl Client where auxiliary, fork_choice, import_existing, + allow_missing_parent, ); if let Ok(ImportResult::Imported(ref aux)) = result { @@ -690,11 +692,14 @@ impl Client where aux: Vec<(Vec, Option>)>, fork_choice: ForkChoiceStrategy, import_existing: bool, + allow_missing_parent: bool, ) -> sp_blockchain::Result where Self: ProvideRuntimeApi, >::Api: CoreApi + ApiExt, { + operation.op.allow_missing_parent(allow_missing_parent); + let parent_hash = import_headers.post().parent_hash().clone(); let status = self.backend.blockchain().status(BlockId::Hash(hash))?; match (import_existing, status) { @@ -776,7 +781,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 &&!allow_missing_parent { let route_from_best = sp_blockchain::tree_route( self.backend.blockchain(), info.best_hash, @@ -839,8 +844,14 @@ impl Client where { let parent_hash = import_block.header.parent_hash(); let at = BlockId::Hash(*parent_hash); + let number = import_block.header.number(); + let hash = import_block.header.hash(); let enact_state = match self.block_status(&at)? { - BlockStatus::Unknown => return Ok(Some(ImportResult::UnknownParent)), + BlockStatus::Unknown if import_block.allow_missing_parent => true, + BlockStatus::Unknown => { + warn!("Block with unknown parent {}: {:?}, parent: {:?}", number, hash, parent_hash); + return Ok(Some(ImportResult::UnknownParent)) + }, BlockStatus::InChainWithState | BlockStatus::Queued => true, BlockStatus::InChainPruned if import_block.allow_missing_state => false, BlockStatus::InChainPruned => return Ok(Some(ImportResult::MissingState)), diff --git a/primitives/consensus/common/src/block_import.rs b/primitives/consensus/common/src/block_import.rs index 5e593da1163d7..fcbd79b85d3a8 100644 --- a/primitives/consensus/common/src/block_import.rs +++ b/primitives/consensus/common/src/block_import.rs @@ -162,6 +162,7 @@ pub struct BlockImportParams { pub fork_choice: Option, /// Allow importing the block skipping state verification if parent state is missing. pub allow_missing_state: bool, + pub allow_missing_parent: bool, /// Re-validate existing block. pub import_existing: bool, /// Cached full header hash (with post-digests applied). @@ -185,6 +186,7 @@ impl BlockImportParams { auxiliary: Vec::new(), fork_choice: None, allow_missing_state: false, + allow_missing_parent: false, import_existing: false, post_hash: None, } @@ -224,6 +226,7 @@ impl BlockImportParams { auxiliary: self.auxiliary, intermediates: self.intermediates, allow_missing_state: self.allow_missing_state, + allow_missing_parent: self.allow_missing_parent, fork_choice: self.fork_choice, import_existing: self.import_existing, post_hash: self.post_hash, diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index 1d01c53417649..77ed87bfe17fe 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -166,6 +166,21 @@ impl ForkTree where Ok(RemovedIterator { stack: removed }) } + + pub fn filter(&self, number: N) -> ForkTree { + let mut roots = vec![]; + + for root in &self.roots { + if root.number <= number { + roots.push(root.filter(number.clone())); + } + } + + ForkTree { + roots, + best_finalized_number: self.best_finalized_number.clone(), + } + } } impl ForkTree where @@ -651,6 +666,25 @@ mod node_implementation { pub children: Vec>, } + impl Node { + pub fn filter(&self, number: N) -> Node { + let mut children = vec![]; + + for node in &self.children { + if node.number <= number { + children.push(node.filter(number.clone())); + } + } + + Node { + hash: self.hash.clone(), + number: self.number.clone(), + data: self.data.clone(), + children, + } + } + } + impl Node { /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). pub fn rebalance(&mut self) {