// Copyright 2017-2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . //! Substrate Client use std::{ marker::PhantomData, collections::{HashSet, BTreeMap, HashMap}, sync::Arc, panic::UnwindSafe, result, cell::RefCell, rc::Rc, }; use log::{info, trace, warn}; use futures03::channel::mpsc; use parking_lot::{Mutex, RwLock}; use codec::{Encode, Decode}; use hash_db::{Hasher, Prefix}; use primitives::{ Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue, ExecutionContext, NativeOrEncoded, storage::{StorageKey, StorageData, well_known_keys}, offchain::{NeverOffchainExt, self}, traits::CodeExecutor, }; use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; use sr_primitives::{ Justification, BuildStorage, generic::{BlockId, SignedBlock, DigestItem}, traits::{ Block as BlockT, Header as HeaderT, Zero, NumberFor, ApiRef, ProvideRuntimeApi, SaturatedConversion, One, DigestFor, }, }; use state_machine::{ DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager, prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, ChangesTrieTransaction, ChangesTrieConfigurationRange, key_changes, key_changes_proof, OverlayedChanges, BackendTrustLevel, }; use executor::{RuntimeVersion, RuntimeInfo}; use consensus::{ Error as ConsensusError, BlockImportParams, ImportResult, BlockOrigin, ForkChoiceStrategy, SelectChain, self, }; use crate::{ runtime_api::{ CallRuntimeAt, ConstructRuntimeApi, Core as CoreApi, ProofRecorder, InitializeBlock, }, backend::{ self, BlockImportOperation, PrunableStateChangesTrieStorage, ClientImportOperation, Finalizer, ImportSummary, }, blockchain::{ self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend, ProvideCache, Cache, well_known_cache_keys::Id as CacheKeyId, }, call_executor::{CallExecutor, LocalCallExecutor}, notifications::{StorageNotifications, StorageEventStream}, light::{call_executor::prove_execution, fetcher::ChangesProof}, block_builder::{self, api::BlockBuilder as BlockBuilderAPI}, error::Error, cht, error, in_mem, genesis }; /// Type that implements `futures::Stream` of block import events. pub type ImportNotifications = mpsc::UnboundedReceiver>; /// A stream of block finality notifications. pub type FinalityNotifications = mpsc::UnboundedReceiver>; type StorageUpdate = < < >::BlockImportOperation as BlockImportOperation >::State as state_machine::Backend>::Transaction; type ChangesUpdate = ChangesTrieTransaction>; /// Execution strategies settings. #[derive(Debug, Clone)] pub struct ExecutionStrategies { /// Execution strategy used when syncing. pub syncing: ExecutionStrategy, /// Execution strategy used when importing blocks. pub importing: ExecutionStrategy, /// Execution strategy used when constructing blocks. pub block_construction: ExecutionStrategy, /// Execution strategy used for offchain workers. pub offchain_worker: ExecutionStrategy, /// Execution strategy used in other cases. pub other: ExecutionStrategy, } impl Default for ExecutionStrategies { fn default() -> ExecutionStrategies { ExecutionStrategies { syncing: ExecutionStrategy::NativeElseWasm, importing: ExecutionStrategy::NativeElseWasm, block_construction: ExecutionStrategy::AlwaysWasm, offchain_worker: ExecutionStrategy::NativeWhenPossible, other: ExecutionStrategy::NativeElseWasm, } } } /// Substrate Client pub struct Client where Block: BlockT { backend: Arc, executor: E, storage_notifications: Mutex>, import_notification_sinks: Mutex>>>, finality_notification_sinks: Mutex>>>, // holds the block hash currently being imported. TODO: replace this with block queue importing_block: RwLock>, execution_strategies: ExecutionStrategies, _phantom: PhantomData, } /// A source of blockchain events. pub trait BlockchainEvents { /// Get block import event stream. Not guaranteed to be fired for every /// imported block. fn import_notification_stream(&self) -> ImportNotifications; /// Get a stream of finality notifications. Not guaranteed to be fired for every /// finalized block. fn finality_notification_stream(&self) -> FinalityNotifications; /// Get storage changes event stream. /// /// Passing `None` as `filter_keys` subscribes to all storage changes. fn storage_changes_notification_stream( &self, filter_keys: Option<&[StorageKey]>, child_filter_keys: Option<&[(StorageKey, Option>)]>, ) -> error::Result>; } /// Fetch block body by ID. pub trait BlockBody { /// Get block body by ID. Returns `None` if the body is not stored. fn block_body(&self, id: &BlockId ) -> error::Result::Extrinsic>>>; } /// Provide a list of potential uncle headers for a given block. pub trait ProvideUncles { /// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors. fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor) -> error::Result>; } /// Client info #[derive(Debug)] pub struct ClientInfo { /// Best block hash. pub chain: ChainInfo, /// State Cache Size currently used by the backend pub used_state_cache_size: Option, } /// Block status. #[derive(Debug, PartialEq, Eq)] pub enum BlockStatus { /// Added to the import queue. Queued, /// Already in the blockchain and the state is available. InChainWithState, /// In the blockchain, but the state is not available. InChainPruned, /// Block or parent is known to be bad. KnownBad, /// Not in the queue or the blockchain. Unknown, } /// Summary of an imported block #[derive(Clone, Debug)] pub struct BlockImportNotification { /// Imported block header hash. pub hash: Block::Hash, /// Imported block origin. pub origin: BlockOrigin, /// Imported block header. pub header: Block::Header, /// Is this the new best block. pub is_new_best: bool, /// List of retracted blocks ordered by block number. pub retracted: Vec, } /// Summary of a finalized block. #[derive(Clone, Debug)] pub struct FinalityNotification { /// Imported block header hash. pub hash: Block::Hash, /// Imported block header. pub header: Block::Header, } // used in importing a block, where additional changes are made after the runtime // executed. enum PrePostHeader { // they are the same: no post-runtime digest items. Same(H), // different headers (pre, post). Different(H, H), } impl PrePostHeader { // get a reference to the "pre-header" -- the header as it should be just after the runtime. fn pre(&self) -> &H { match *self { PrePostHeader::Same(ref h) => h, PrePostHeader::Different(ref h, _) => h, } } // get a reference to the "post-header" -- the header as it should be after all changes are applied. fn post(&self) -> &H { match *self { PrePostHeader::Same(ref h) => h, PrePostHeader::Different(_, ref h) => h, } } // convert to the "post-header" -- the header as it should be after all changes are applied. fn into_post(self) -> H { match self { PrePostHeader::Same(h) => h, PrePostHeader::Different(_, h) => h, } } } /// Create an instance of in-memory client. pub fn new_in_mem( executor: E, genesis_storage: S, keystore: Option, ) -> error::Result, LocalCallExecutor, E>, Block, RA >> where E: CodeExecutor + RuntimeInfo, S: BuildStorage, Block: BlockT, { new_with_backend(Arc::new(in_mem::Backend::new()), executor, genesis_storage, keystore) } /// Create a client with the explicitly provided backend. /// This is useful for testing backend implementations. pub fn new_with_backend( backend: Arc, executor: E, build_genesis_storage: S, keystore: Option, ) -> error::Result, Block, RA>> where E: CodeExecutor + RuntimeInfo, S: BuildStorage, Block: BlockT, B: backend::LocalBackend { let call_executor = LocalCallExecutor::new(backend.clone(), executor, keystore); Client::new(backend, call_executor, build_genesis_storage, Default::default()) } /// Figure out the block type for a given type (for now, just a `Client`). pub trait BlockOf { /// The type of the block. type Type: BlockT; } impl BlockOf for Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { type Type = Block; } impl Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { /// Creates new Substrate Client with given blockchain and code executor. pub fn new( backend: Arc, executor: E, build_genesis_storage: S, execution_strategies: ExecutionStrategies ) -> error::Result { if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?; let mut op = backend.begin_operation()?; backend.begin_state_operation(&mut op, BlockId::Hash(Default::default()))?; let state_root = op.reset_storage(genesis_storage, children_genesis_storage)?; let genesis_block = genesis::construct_genesis_block::(state_root.into()); info!("Initializing Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), genesis_block.header().hash() ); op.set_block_data( genesis_block.deconstruct().0, Some(vec![]), None, crate::backend::NewBlockState::Final )?; backend.commit_operation(op)?; } Ok(Client { backend, executor, storage_notifications: Default::default(), import_notification_sinks: Default::default(), finality_notification_sinks: Default::default(), importing_block: Default::default(), execution_strategies, _phantom: Default::default(), }) } /// Get a reference to the execution strategies. pub fn execution_strategies(&self) -> &ExecutionStrategies { &self.execution_strategies } /// Get a reference to the state at a given block. pub fn state_at(&self, block: &BlockId) -> error::Result { self.backend.state_at(*block) } /// Expose backend reference. To be used in tests only #[doc(hidden)] #[deprecated(note="Rather than relying on `client` to provide this, access \ to the backend should be handled at setup only - see #1134. This function \ will be removed once that is in place.")] pub fn backend(&self) -> &Arc { &self.backend } /// Given a `BlockId` and a key prefix, return the matching child storage keys in that block. pub fn storage_keys(&self, id: &BlockId, key_prefix: &StorageKey) -> error::Result> { let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); Ok(keys) } /// Given a `BlockId` and a key, return the value under the key in that block. pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result> { Ok(self.state_at(id)? .storage(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? .map(StorageData) ) } /// Given a `BlockId` and a key, return the value under the hash in that block. pub fn storage_hash(&self, id: &BlockId, key: &StorageKey) -> error::Result> { Ok(self.state_at(id)? .storage_hash(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? ) } /// Given a `BlockId`, a key prefix, and a child storage key, return the matching child storage keys. pub fn child_storage_keys( &self, id: &BlockId, child_storage_key: &StorageKey, key_prefix: &StorageKey ) -> error::Result> { let keys = self.state_at(id)? .child_keys(&child_storage_key.0, &key_prefix.0) .into_iter() .map(StorageKey) .collect(); Ok(keys) } /// Given a `BlockId`, a key and a child storage key, return the value under the key in that block. pub fn child_storage( &self, id: &BlockId, child_storage_key: &StorageKey, key: &StorageKey ) -> error::Result> { Ok(self.state_at(id)? .child_storage(&child_storage_key.0, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? .map(StorageData)) } /// Given a `BlockId`, a key and a child storage key, return the hash under the key in that block. pub fn child_storage_hash( &self, id: &BlockId, child_storage_key: &StorageKey, key: &StorageKey ) -> error::Result> { Ok(self.state_at(id)? .child_storage_hash(&child_storage_key.0, &key.0).map_err(|e| error::Error::from_state(Box::new(e)))? ) } /// Get the code at a given block. pub fn code_at(&self, id: &BlockId) -> error::Result> { Ok(self.storage(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) } /// Get the RuntimeVersion at a given block. pub fn runtime_version_at(&self, id: &BlockId) -> error::Result { self.executor.runtime_version(id) } /// Get call executor reference. pub fn executor(&self) -> &E { &self.executor } /// Reads storage value at a given block + key, returning read proof. pub fn read_proof(&self, id: &BlockId, keys: I) -> error::Result>> where I: IntoIterator, I::Item: AsRef<[u8]>, { self.state_at(id) .and_then(|state| prove_read(state, keys) .map_err(Into::into)) } /// Reads child storage value at a given block + storage_key + key, returning /// read proof. pub fn read_child_proof( &self, id: &BlockId, storage_key: &[u8], keys: I, ) -> error::Result>> where I: IntoIterator, I::Item: AsRef<[u8]>, { self.state_at(id) .and_then(|state| prove_child_read(state, storage_key, keys) .map_err(Into::into)) } /// Execute a call to a contract on top of state in a block of given hash /// AND returning execution proof. /// /// No changes are made. pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8] ) -> error::Result<(Vec, Vec>)> { let state = self.state_at(id)?; let header = self.prepare_environment_block(id)?; prove_execution(state, header, &self.executor, method, call_data) } /// Reads given header and generates CHT-based header proof. pub fn header_proof(&self, id: &BlockId) -> error::Result<(Block::Header, Vec>)> { self.header_proof_with_cht_size(id, cht::size()) } /// Get block hash by number. pub fn block_hash(&self, block_number: <::Header as HeaderT>::Number ) -> error::Result> { self.backend.blockchain().hash(block_number) } /// Reads given header and generates CHT-based header proof for CHT of given size. pub fn header_proof_with_cht_size( &self, id: &BlockId, cht_size: NumberFor, ) -> error::Result<(Block::Header, Vec>)> { let proof_error = || error::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)?; let cht_start = cht::start_number(cht_size, cht_num); let mut current_num = cht_start; let cht_range = ::std::iter::from_fn(|| { let old_current_num = current_num; current_num = current_num + One::one(); Some(old_current_num) }); let headers = cht_range.map(|num| self.block_hash(num)); let proof = cht::build_proof::(cht_size, cht_num, ::std::iter::once(block_num), headers)?; Ok((header, proof)) } /// Get longest range within [first; last] that is possible to use in `key_changes` /// and `key_changes_proof` calls. /// Range could be shortened from the beginning if some changes tries have been pruned. /// Returns Ok(None) if changes trues are not supported. pub fn max_key_changes_range( &self, first: NumberFor, last: BlockId, ) -> error::Result, BlockId)>> { let (config, storage) = match self.require_changes_trie().ok() { Some((config, storage)) => (config, storage), None => return Ok(None), }; let last_num = self.backend.blockchain().expect_block_number_from_id(&last)?; if first > last_num { return Err(error::Error::ChangesTrieAccessFailed("Invalid changes trie range".into())); } let finalized_number = self.backend.blockchain().info().finalized_number; let oldest = storage.oldest_changes_trie_block(&config, finalized_number); let first = ::std::cmp::max(first, oldest); Ok(Some((first, last))) } /// Get pairs of (block, extrinsic) where key has been changed at given blocks range. /// Works only for runtimes that are supporting changes tries. /// /// Changes are returned in descending order (i.e. last block comes first). pub fn key_changes( &self, first: NumberFor, last: BlockId, storage_key: Option<&StorageKey>, key: &StorageKey ) -> error::Result, u32)>> { let (config, storage) = self.require_changes_trie()?; let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?; let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?; // FIXME: remove this in https://github.com/paritytech/substrate/pull/3201 let config_range = ChangesTrieConfigurationRange { config: &config, zero: Zero::zero(), end: None, }; key_changes::( config_range, &*storage, first, &ChangesTrieAnchorBlockId { hash: convert_hash(&last_hash), number: last_number, }, self.backend.blockchain().info().best_number, storage_key.as_ref().map(|sk| sk.0.as_slice()), &key.0) .and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::>()) .map_err(|err| error::Error::ChangesTrieAccessFailed(err)) } /// Get proof for computation of (block, extrinsic) pairs where key has been changed at given blocks range. /// `min` is the hash of the first block, which changes trie root is known to the requester - when we're using /// changes tries from ascendants of this block, we should provide proofs for changes tries roots /// `max` is the hash of the last block known to the requester - we can't use changes tries from descendants /// of this block. /// Works only for runtimes that are supporting changes tries. pub fn key_changes_proof( &self, first: Block::Hash, last: Block::Hash, min: Block::Hash, max: Block::Hash, storage_key: Option<&StorageKey>, key: &StorageKey, ) -> error::Result> { self.key_changes_proof_with_cht_size( first, last, min, max, storage_key, key, cht::size(), ) } /// Does the same work as `key_changes_proof`, but assumes that CHTs are of passed size. pub fn key_changes_proof_with_cht_size( &self, first: Block::Hash, last: Block::Hash, min: Block::Hash, max: Block::Hash, storage_key: Option<&StorageKey>, key: &StorageKey, cht_size: NumberFor, ) -> error::Result> { struct AccessedRootsRecorder<'a, Block: BlockT> { storage: &'a dyn ChangesTrieStorage>, min: NumberFor, required_roots_proofs: Mutex, H256>>, }; impl<'a, Block: BlockT> ChangesTrieRootsStorage> for AccessedRootsRecorder<'a, Block> { fn build_anchor(&self, hash: H256) -> Result>, String> { self.storage.build_anchor(hash) } fn root( &self, anchor: &ChangesTrieAnchorBlockId>, block: NumberFor, ) -> Result, String> { 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() ); } } Ok(root) } } impl<'a, Block: BlockT> ChangesTrieStorage> for AccessedRootsRecorder<'a, Block> { fn as_roots_storage(&self) -> &dyn state_machine::ChangesTrieRootsStorage> { self } fn with_cached_changed_keys( &self, root: &H256, functor: &mut dyn FnMut(&HashMap>, HashSet>>), ) -> bool { self.storage.with_cached_changed_keys(root, functor) } fn get(&self, key: &H256, prefix: Prefix) -> Result, String> { self.storage.get(key, prefix) } } let (config, storage) = self.require_changes_trie()?; let min_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?; let recording_storage = AccessedRootsRecorder:: { storage, min: min_number, required_roots_proofs: Mutex::new(BTreeMap::new()), }; let max_number = ::std::cmp::min( self.backend.blockchain().info().best_number, self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(max))?, ); // FIXME: remove this in https://github.com/paritytech/substrate/pull/3201 let config_range = ChangesTrieConfigurationRange { config: &config, zero: Zero::zero(), end: None, }; // fetch key changes proof let first_number = self.backend.blockchain() .expect_block_number_from_id(&BlockId::Hash(first))?; let last_number = self.backend.blockchain() .expect_block_number_from_id(&BlockId::Hash(last))?; let key_changes_proof = key_changes_proof::( config_range, &recording_storage, first_number, &ChangesTrieAnchorBlockId { hash: convert_hash(&last), number: last_number, }, max_number, storage_key.as_ref().map(|sk| sk.0.as_slice()), &key.0, ) .map_err(|err| error::Error::from(error::Error::ChangesTrieAccessFailed(err)))?; // now gather proofs for all changes tries roots that were touched during key_changes_proof // execution AND are unknown (i.e. replaced with CHT) to the requester let roots = recording_storage.required_roots_proofs.into_inner(); let roots_proof = self.changes_trie_roots_proof(cht_size, roots.keys().cloned())?; Ok(ChangesProof { max_block: max_number, proof: key_changes_proof, roots: roots.into_iter().map(|(n, h)| (n, convert_hash(&h))).collect(), roots_proof, }) } /// Generate CHT-based proof for roots of changes tries at given blocks. fn changes_trie_roots_proof>>( &self, cht_size: NumberFor, blocks: I ) -> error::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 proof = HashSet::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)?; proof.extend(cht_proof); Ok(()) }, ())?; Ok(proof.into_iter().collect()) } /// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT). fn changes_trie_roots_proof_at_cht( &self, cht_size: NumberFor, cht_num: NumberFor, blocks: Vec> ) -> error::Result>> { let cht_start = cht::start_number(cht_size, cht_num); let mut current_num = cht_start; let cht_range = ::std::iter::from_fn(|| { let old_current_num = current_num; 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 proof = cht::build_proof::(cht_size, cht_num, blocks, roots)?; Ok(proof) } /// Returns changes trie configuration and storage or an error if it is not supported. fn require_changes_trie(&self) -> error::Result<(ChangesTrieConfiguration, &B::ChangesTrieStorage)> { let config = self.changes_trie_config()?; let storage = self.backend.changes_trie_storage(); match (config, storage) { (Some(config), Some(storage)) => Ok((config, storage)), _ => Err(error::Error::ChangesTriesNotSupported.into()), } } /// Create a new block, built on the head of the chain. pub fn new_block( &self, inherent_digests: DigestFor, ) -> error::Result> where E: Clone + Send + Sync, RA: Send + Sync, Self: ProvideRuntimeApi, ::Api: BlockBuilderAPI { block_builder::BlockBuilder::new(self, inherent_digests) } /// Create a new block, built on top of `parent`. pub fn new_block_at( &self, parent: &BlockId, inherent_digests: DigestFor, ) -> error::Result> where E: Clone + Send + Sync, RA: Send + Sync, Self: ProvideRuntimeApi, ::Api: BlockBuilderAPI { block_builder::BlockBuilder::at_block(parent, &self, false, inherent_digests) } /// Create a new block, built on top of `parent` with proof recording enabled. /// /// While proof recording is enabled, all accessed trie nodes are saved. /// These recorded trie nodes can be used by a third party to proof the /// output of this block builder without having access to the full storage. pub fn new_block_at_with_proof_recording( &self, parent: &BlockId, inherent_digests: DigestFor, ) -> error::Result> where E: Clone + Send + Sync, RA: Send + Sync, Self: ProvideRuntimeApi, ::Api: BlockBuilderAPI { block_builder::BlockBuilder::at_block(parent, &self, true, inherent_digests) } /// Lock the import lock, and run operations inside. pub fn lock_import_and_run(&self, f: F) -> Result where F: FnOnce(&mut ClientImportOperation) -> Result, Err: From, { let inner = || { let _import_lock = self.backend.get_import_lock().lock(); let mut op = ClientImportOperation { op: self.backend.begin_operation()?, notify_imported: None, notify_finalized: Vec::new(), }; let r = f(&mut op)?; let ClientImportOperation { op, notify_imported, notify_finalized } = op; self.backend.commit_operation(op)?; self.notify_finalized(notify_finalized)?; if let Some(notify_imported) = notify_imported { self.notify_imported(notify_imported)?; } Ok(r) }; let result = inner(); *self.importing_block.write() = None; result } /// Apply a checked and validated block to an operation. If a justification is provided /// then `finalized` *must* be true. fn apply_block( &self, operation: &mut ClientImportOperation, import_block: BlockImportParams, new_cache: HashMap>, ) -> error::Result where E: CallExecutor + Send + Sync + Clone, { let BlockImportParams { origin, header, justification, post_digests, body, finalized, auxiliary, fork_choice, } = import_block; assert!(justification.is_some() && finalized || justification.is_none()); let parent_hash = header.parent_hash().clone(); match self.backend.blockchain().status(BlockId::Hash(parent_hash))? { blockchain::BlockStatus::InChain => {}, blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), } let import_headers = if post_digests.is_empty() { PrePostHeader::Same(header) } else { let mut post_header = header.clone(); for item in post_digests { post_header.digest_mut().push(item); } PrePostHeader::Different(header, post_header) }; let hash = import_headers.post().hash(); let height = (*import_headers.post().number()).saturated_into::(); *self.importing_block.write() = Some(hash); let result = self.execute_and_import_block( operation, origin, hash, import_headers, justification, body, new_cache, finalized, auxiliary, fork_choice, ); if let Ok(ImportResult::Imported(ref aux)) = result { if aux.is_new_best { telemetry!(SUBSTRATE_INFO; "block.import"; "height" => height, "best" => ?hash, "origin" => ?origin ); } } result } fn execute_and_import_block( &self, operation: &mut ClientImportOperation, origin: BlockOrigin, hash: Block::Hash, import_headers: PrePostHeader, justification: Option, body: Option>, new_cache: HashMap>, finalized: bool, aux: Vec<(Vec, Option>)>, fork_choice: ForkChoiceStrategy, ) -> error::Result where E: CallExecutor + Send + Sync + Clone, { let parent_hash = import_headers.post().parent_hash().clone(); match self.backend.blockchain().status(BlockId::Hash(hash))? { blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain), blockchain::BlockStatus::Unknown => {}, } let info = self.backend.blockchain().info(); // the block is lower than our last finalized block so it must revert // finality, refusing import. if *import_headers.post().number() <= info.finalized_number { return Err(error::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::Genesis | BlockOrigin::NetworkInitialSync | BlockOrigin::File => false, }; self.backend.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?; // 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)?; } // FIXME #1232: correct path logic for when to execute this function let (storage_update, changes_update, storage_changes) = self.block_execution( &operation.op, &import_headers, origin, hash, body.clone(), )?; 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 { crate::backend::NewBlockState::Final } else if is_new_best { crate::backend::NewBlockState::Best } else { crate::backend::NewBlockState::Normal }; let retracted = if is_new_best { let route_from_best = crate::blockchain::tree_route( |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), BlockId::Hash(info.best_hash), BlockId::Hash(parent_hash), )?; route_from_best.retracted().iter().rev().map(|e| e.hash.clone()).collect() } else { Vec::default() }; trace!("Imported {}, (#{}), best={}, origin={:?}", hash, import_headers.post().number(), is_new_best, origin); operation.op.set_block_data( import_headers.post().clone(), body, justification, leaf_state, )?; operation.op.update_cache(new_cache); if let Some(storage_update) = storage_update { operation.op.update_db_storage(storage_update)?; } if let Some(storage_changes) = storage_changes.clone() { operation.op.update_storage(storage_changes.0, storage_changes.1)?; } if let Some(Some(changes_update)) = changes_update { operation.op.update_changes_trie(changes_update)?; } operation.op.insert_aux(aux)?; if make_notifications { if finalized { operation.notify_finalized.push(hash); } operation.notify_imported = Some(ImportSummary { hash, origin, header: import_headers.into_post(), is_new_best, storage_changes, retracted, }) } Ok(ImportResult::imported(is_new_best)) } fn block_execution( &self, transaction: &B::BlockImportOperation, import_headers: &PrePostHeader, origin: BlockOrigin, hash: Block::Hash, body: Option>, ) -> error::Result<( Option>, Option>>, Option<( Vec<(Vec, Option>)>, Vec<(Vec, Vec<(Vec, Option>)>)> )> )> where E: CallExecutor + Send + Sync + Clone, { match transaction.state()? { Some(transaction_state) => { let mut overlay = Default::default(); let get_execution_manager = |execution_strategy: ExecutionStrategy| { match execution_strategy { ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm, ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted), ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { let header = import_headers.post(); warn!("Consensus error between wasm and native block execution at block {}", hash); warn!(" Header {:?}", header); warn!(" Native result {:?}", native_result); warn!(" Wasm result {:?}", wasm_result); telemetry!(SUBSTRATE_INFO; "block.execute.consensus_failure"; "hash" => ?hash, "origin" => ?origin, "header" => ?header ); wasm_result }), } }; let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, _, NeverNativeValue, fn() -> _>( transaction_state, &mut overlay, "Core_execute_block", &::new(import_headers.pre().clone(), body.unwrap_or_default()).encode(), match origin { BlockOrigin::NetworkInitialSync => get_execution_manager(self.execution_strategies().syncing), _ => get_execution_manager(self.execution_strategies().importing), }, None, NeverOffchainExt::new(), )?; overlay.commit_prospective(); let (top, children) = overlay.into_committed(); let children = children.map(|(sk, it)| (sk, it.collect())).collect(); if import_headers.post().state_root() != &storage_update.1 { return Err(error::Error::InvalidStateRoot); } Ok((Some(storage_update.0), Some(changes_update), Some((top.collect(), children)))) }, None => Ok((None, None, None)) } } fn apply_finality_with_block_hash( &self, operation: &mut ClientImportOperation, block: Block::Hash, justification: Option, best_block: Block::Hash, notify: bool, ) -> error::Result<()> { // find tree route from last finalized to given block. 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(()); } let route_from_finalized = crate::blockchain::tree_route( |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), BlockId::Hash(last_finalized), BlockId::Hash(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); return Err(error::Error::NotInFinalizedChain); } let route_from_best = crate::blockchain::tree_route( |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), BlockId::Hash(best_block), BlockId::Hash(block), )?; // if the block is not a direct ancestor of the current best chain, // then some other block is the common ancestor. if route_from_best.common_block().hash != block { // NOTE: we're setting the finalized block as best block, this might // be slightly innacurate since we might have a "better" block // further along this chain, but since best chain selection logic is // pluggable we cannot make a better choice here. usages that need // an accurate "best" block need to go through `SelectChain` // instead. operation.op.mark_head(BlockId::Hash(block))?; } let enacted = route_from_finalized.enacted(); assert!(enacted.len() > 0); for finalize_new in &enacted[..enacted.len() - 1] { operation.op.mark_finalized(BlockId::Hash(finalize_new.hash), None)?; } assert_eq!(enacted.last().map(|e| e.hash), Some(block)); operation.op.mark_finalized(BlockId::Hash(block), justification)?; if notify { // sometimes when syncing, tons of blocks can be finalized at once. // we'll send notifications spuriously in that case. const MAX_TO_NOTIFY: usize = 256; let enacted = route_from_finalized.enacted(); let start = enacted.len() - ::std::cmp::min(enacted.len(), MAX_TO_NOTIFY); for finalized in &enacted[start..] { operation.notify_finalized.push(finalized.hash); } } Ok(()) } fn notify_finalized( &self, notify_finalized: Vec, ) -> error::Result<()> { let mut sinks = self.finality_notification_sinks.lock(); 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"); telemetry!(SUBSTRATE_INFO; "notify.finalized"; "height" => format!("{}", header.number()), "best" => ?finalized_hash, ); let notification = FinalityNotification { header, hash: finalized_hash, }; sinks.retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); } Ok(()) } fn notify_imported( &self, notify_import: ImportSummary, ) -> error::Result<()> { 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())), ); } let notification = BlockImportNotification:: { hash: notify_import.hash, origin: notify_import.origin, header: notify_import.header, is_new_best: notify_import.is_new_best, retracted: notify_import.retracted, }; self.import_notification_sinks.lock() .retain(|sink| sink.unbounded_send(notification.clone()).is_ok()); Ok(()) } /// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were /// successfully reverted. pub fn revert(&self, n: NumberFor) -> error::Result> { Ok(self.backend.revert(n)?) } /// Get blockchain info. pub fn info(&self) -> ClientInfo { let info = self.backend.blockchain().info(); ClientInfo { chain: info, used_state_cache_size: self.backend.used_state_cache_size(), } } /// Get block status. pub fn block_status(&self, id: &BlockId) -> error::Result { // 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); } } let hash_and_number = match id.clone() { BlockId::Hash(hash) => self.backend.blockchain().number(hash)?.map(|n| (hash, n)), BlockId::Number(n) => self.backend.blockchain().hash(n)?.map(|hash| (hash, n)), }; match hash_and_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) -> error::Result::Header>> { self.backend.blockchain().header(*id) } /// Get block body by id. pub fn body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { self.backend.blockchain().body(*id) } /// Get block justification set by id. pub fn justification(&self, id: &BlockId) -> error::Result> { self.backend.blockchain().justification(*id) } /// Get full block by id. pub fn block(&self, id: &BlockId) -> error::Result>> { Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) { (Some(header), Some(extrinsics), justification) => Some(SignedBlock { block: Block::new(header, extrinsics), justification }), _ => None, }) } /// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors. pub fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor) -> error::Result> { let load_header = |id: Block::Hash| -> error::Result { match self.backend.blockchain().header(BlockId::Hash(id))? { Some(hdr) => Ok(hdr), None => Err(Error::UnknownBlock(format!("{:?}", id))), } }; let genesis_hash = self.backend.blockchain().info().genesis_hash; if genesis_hash == target_hash { return Ok(Vec::new()); } let mut current_hash = target_hash; let mut current = load_header(current_hash)?; let mut ancestor_hash = *current.parent_hash(); let mut ancestor = load_header(ancestor_hash)?; let mut uncles = Vec::new(); for _generation in 0..max_generation.saturated_into() { let children = self.backend.blockchain().children(ancestor_hash)?; uncles.extend(children.into_iter().filter(|h| h != ¤t_hash)); current_hash = ancestor_hash; if genesis_hash == current_hash { break; } current = ancestor; ancestor_hash = *current.parent_hash(); ancestor = load_header(ancestor_hash)?; } trace!("Collected {} uncles", uncles.len()); Ok(uncles) } fn changes_trie_config(&self) -> Result, Error> { Ok(self.backend.state_at(BlockId::Number(self.backend.blockchain().info().best_number))? .storage(well_known_keys::CHANGES_TRIE_CONFIG) .map_err(|e| error::Error::from_state(Box::new(e)))? .and_then(|c| Decode::decode(&mut &*c).ok())) } /// Prepare in-memory header that is used in execution environment. fn prepare_environment_block(&self, parent: &BlockId) -> error::Result { let parent_header = self.backend.blockchain().expect_header(*parent)?; Ok(<::Header as HeaderT>::new( self.backend.blockchain().expect_block_number_from_id(parent)? + One::one(), Default::default(), Default::default(), parent_header.hash(), Default::default(), )) } } impl ProvideUncles for Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor) -> error::Result> { Ok(Client::uncles(self, target_hash, max_generation)? .into_iter() .filter_map(|hash| Client::header(self, &BlockId::Hash(hash)).unwrap_or(None)) .collect() ) } } impl ChainHeaderBackend for Client where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, RA: Send + Sync { fn header(&self, id: BlockId) -> error::Result> { self.backend.blockchain().header(id) } fn info(&self) -> blockchain::Info { self.backend.blockchain().info() } fn status(&self, id: BlockId) -> error::Result { self.backend.blockchain().status(id) } fn number(&self, hash: Block::Hash) -> error::Result::Header as HeaderT>::Number>> { self.backend.blockchain().number(hash) } fn hash(&self, number: NumberFor) -> error::Result> { self.backend.blockchain().hash(number) } } impl ChainHeaderBackend for &Client where B: backend::Backend, E: CallExecutor + Send + Sync, Block: BlockT, RA: Send + Sync, { fn header(&self, id: BlockId) -> error::Result> { (**self).backend.blockchain().header(id) } fn info(&self) -> blockchain::Info { (**self).backend.blockchain().info() } fn status(&self, id: BlockId) -> error::Result { (**self).status(id) } fn number(&self, hash: Block::Hash) -> error::Result::Header as HeaderT>::Number>> { (**self).number(hash) } fn hash(&self, number: NumberFor) -> error::Result> { (**self).hash(number) } } impl ProvideCache for Client where B: backend::Backend, Block: BlockT, { fn cache(&self) -> Option>> { self.backend.blockchain().cache() } } impl ProvideRuntimeApi for Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, Block: BlockT, RA: ConstructRuntimeApi { type Api = >::RuntimeApi; fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> { RA::construct_runtime_api(self) } } impl CallRuntimeAt for Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, Block: BlockT, { fn call_api_at< 'a, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, C: CoreApi, >( &self, core_api: &C, at: &BlockId, function: &'static str, args: Vec, changes: &RefCell, initialize_block: InitializeBlock<'a, Block>, native_call: Option, context: ExecutionContext, recorder: &Option>>>, ) -> error::Result> { let manager = match context { ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(), ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(), ExecutionContext::Importing => self.execution_strategies.importing.get_manager(), ExecutionContext::OffchainCall(Some((_, capabilities))) if capabilities.has_all() => self.execution_strategies.offchain_worker.get_manager(), ExecutionContext::OffchainCall(_) => self.execution_strategies.other.get_manager(), }; let capabilities = context.capabilities(); let mut offchain_extensions = match context { ExecutionContext::OffchainCall(ext) => ext.map(|x| x.0), _ => None, }.map(|ext| offchain::LimitedExternalities::new(capabilities, ext)); self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>( || core_api.initialize_block(at, &self.prepare_environment_block(at)?), at, function, &args, changes, initialize_block, manager, native_call, offchain_extensions.as_mut(), recorder, capabilities.has(offchain::Capability::Keystore), ) } fn runtime_version_at(&self, at: &BlockId) -> error::Result { self.runtime_version_at(at) } } impl<'a, B, E, Block, RA> consensus::BlockImport for &'a Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, Block: BlockT, { type Error = ConsensusError; /// Import a checked and validated block. If a justification is provided in /// `BlockImportParams` then `finalized` *must* be true. fn import_block( &mut self, import_block: BlockImportParams, new_cache: HashMap>, ) -> Result { self.lock_import_and_run(|operation| { self.apply_block(operation, import_block, new_cache) }).map_err(|e| { warn!("Block import error:\n{:?}", e); ConsensusError::ClientImport(e.to_string()).into() }) } /// Check block preconditions. fn check_block( &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { match self.block_status(&BlockId::Hash(parent_hash)) .map_err(|e| ConsensusError::ClientImport(e.to_string()))? { BlockStatus::InChainWithState | BlockStatus::Queued => {}, BlockStatus::Unknown | BlockStatus::InChainPruned => return Ok(ImportResult::UnknownParent), BlockStatus::KnownBad => return Ok(ImportResult::KnownBad), } match self.block_status(&BlockId::Hash(hash)) .map_err(|e| ConsensusError::ClientImport(e.to_string()))? { BlockStatus::InChainWithState | BlockStatus::Queued => return Ok(ImportResult::AlreadyInChain), BlockStatus::Unknown | BlockStatus::InChainPruned => {}, BlockStatus::KnownBad => return Ok(ImportResult::KnownBad), } Ok(ImportResult::imported(false)) } } impl consensus::BlockImport for Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, Block: BlockT, { type Error = ConsensusError; fn import_block( &mut self, import_block: BlockImportParams, new_cache: HashMap>, ) -> Result { (&*self).import_block(import_block, new_cache) } fn check_block( &mut self, hash: Block::Hash, parent_hash: Block::Hash, ) -> Result { (&*self).check_block(hash, parent_hash) } } impl Finalizer for Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { fn apply_finality( &self, operation: &mut ClientImportOperation, id: BlockId, justification: Option, notify: bool, ) -> error::Result<()> { let last_best = self.backend.blockchain().info().best_hash; let to_finalize_hash = self.backend.blockchain().expect_block_hash_from_id(&id)?; self.apply_finality_with_block_hash(operation, to_finalize_hash, justification, last_best, notify) } fn finalize_block(&self, id: BlockId, justification: Option, notify: bool) -> error::Result<()> { self.lock_import_and_run(|operation| { self.apply_finality(operation, id, justification, notify) }) } } impl Finalizer for &Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { fn apply_finality( &self, operation: &mut ClientImportOperation, id: BlockId, justification: Option, notify: bool, ) -> error::Result<()> { (**self).apply_finality(operation, id, justification, notify) } fn finalize_block(&self, id: BlockId, justification: Option, notify: bool) -> error::Result<()> { (**self).finalize_block(id, justification, notify) } } impl BlockchainEvents for Client where E: CallExecutor, Block: BlockT, { /// Get block import event stream. fn import_notification_stream(&self) -> ImportNotifications { let (sink, stream) = mpsc::unbounded(); self.import_notification_sinks.lock().push(sink); stream } fn finality_notification_stream(&self) -> FinalityNotifications { let (sink, stream) = mpsc::unbounded(); self.finality_notification_sinks.lock().push(sink); stream } /// Get storage changes event stream. fn storage_changes_notification_stream( &self, filter_keys: Option<&[StorageKey]>, child_filter_keys: Option<&[(StorageKey, Option>)]>, ) -> error::Result> { Ok(self.storage_notifications.lock().listen(filter_keys, child_filter_keys)) } } /// Implement Longest Chain Select implementation /// where 'longest' is defined as the highest number of blocks pub struct LongestChain { backend: Arc, _phantom: PhantomData } impl Clone for LongestChain { fn clone(&self) -> Self { let backend = self.backend.clone(); LongestChain { backend, _phantom: Default::default() } } } impl LongestChain where B: backend::Backend, Block: BlockT, { /// Instantiate a new LongestChain for Backend B pub fn new(backend: Arc) -> Self { LongestChain { backend, _phantom: Default::default() } } fn best_block_header(&self) -> error::Result<::Header> { let info = self.backend.blockchain().info(); let best_hash = self.best_containing(info.best_hash, None)? .unwrap_or(info.best_hash); Ok(self.backend.blockchain().header(BlockId::Hash(best_hash))? .expect("given block hash was fetched from block in db; qed")) } /// Get the most recent block hash of the best (longest) chains /// that contain block with the given `target_hash`. /// /// The search space is always limited to blocks which are in the finalized /// chain or descendents of it. /// /// If `maybe_max_block_number` is `Some(max_block_number)` /// the search is limited to block `numbers <= max_block_number`. /// in other words as if there were no blocks greater `max_block_number`. /// Returns `Ok(None)` if `target_hash` is not found in search space. /// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444) fn best_containing( &self, target_hash: Block::Hash, maybe_max_number: Option> ) -> error::Result> { let target_header = { match self.backend.blockchain().header(BlockId::Hash(target_hash))? { Some(x) => x, // target not in blockchain None => { return Ok(None); }, } }; if let Some(max_number) = maybe_max_number { // target outside search range if target_header.number() > &max_number { return Ok(None); } } let leaves = { // ensure no blocks are imported during this code block. // an import could trigger a reorg which could change the canonical chain. // we depend on the canonical chain staying the same during this code block. let _import_lock = self.backend.get_import_lock().lock(); let info = self.backend.blockchain().info(); // this can be `None` if the best chain is shorter than the target header. let maybe_canon_hash = self.backend.blockchain().hash(*target_header.number())?; if maybe_canon_hash.as_ref() == Some(&target_hash) { // if a `max_number` is given we try to fetch the block at the // given depth, if it doesn't exist or `max_number` is not // provided, we continue to search from all leaves below. if let Some(max_number) = maybe_max_number { if let Some(header) = self.backend.blockchain().hash(max_number)? { return Ok(Some(header)); } } } else if info.finalized_number >= *target_header.number() { // header is on a dead fork. return Ok(None); } self.backend.blockchain().leaves()? }; // for each chain. longest chain first. shortest last for leaf_hash in leaves { // start at the leaf let mut current_hash = leaf_hash; // if search is not restricted then the leaf is the best let mut best_hash = leaf_hash; // go backwards entering the search space // waiting until we are <= max_number if let Some(max_number) = maybe_max_number { loop { let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))? .ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?; if current_header.number() <= &max_number { best_hash = current_header.hash(); break; } current_hash = *current_header.parent_hash(); } } // go backwards through the chain (via parent links) loop { // until we find target if current_hash == target_hash { return Ok(Some(best_hash)); } let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))? .ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?; // stop search in this chain once we go below the target's block number if current_header.number() < target_header.number() { break; } current_hash = *current_header.parent_hash(); } } // header may be on a dead fork -- the only leaves that are considered are // those which can still be finalized. // // FIXME #1558 only issue this warning when not on a dead fork warn!( "Block {:?} exists in chain but not found when following all \ leaves backwards. Number limit = {:?}", target_hash, maybe_max_number, ); Ok(None) } fn leaves(&self) -> Result::Hash>, error::Error> { self.backend.blockchain().leaves() } } impl SelectChain for LongestChain where B: backend::Backend, Block: BlockT, { fn leaves(&self) -> Result::Hash>, ConsensusError> { LongestChain::leaves(self) .map_err(|e| ConsensusError::ChainLookup(e.to_string()).into()) } fn best_chain(&self) -> Result<::Header, ConsensusError> { LongestChain::best_block_header(&self) .map_err(|e| ConsensusError::ChainLookup(e.to_string()).into()) } fn finality_target( &self, target_hash: Block::Hash, maybe_max_number: Option> ) -> Result, ConsensusError> { LongestChain::best_containing(self, target_hash, maybe_max_number) .map_err(|e| ConsensusError::ChainLookup(e.to_string()).into()) } } impl BlockBody for Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { fn block_body(&self, id: &BlockId) -> error::Result::Extrinsic>>> { self.body(id) } } impl backend::AuxStore for Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { /// Insert auxiliary data into key-value store. fn insert_aux< 'a, 'b: 'a, 'c: 'a, I: IntoIterator, D: IntoIterator, >(&self, insert: I, delete: D) -> error::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) }) } /// Query auxiliary data from key-value store. fn get_aux(&self, key: &[u8]) -> error::Result>> { crate::backend::AuxStore::get_aux(&*self.backend, key) } } impl backend::AuxStore for &Client where B: backend::Backend, E: CallExecutor, Block: BlockT, { fn insert_aux< 'a, 'b: 'a, 'c: 'a, I: IntoIterator, D: IntoIterator, >(&self, insert: I, delete: D) -> error::Result<()> { (**self).insert_aux(insert, delete) } fn get_aux(&self, key: &[u8]) -> error::Result>> { (**self).get_aux(key) } } /// Helper function to apply auxiliary data insertion into an operation. pub fn apply_aux<'a, 'b: 'a, 'c: 'a, B, Block, H, D, I>( operation: &mut ClientImportOperation, insert: I, delete: D ) -> error::Result<()> where Block: BlockT, H: Hasher, B: backend::Backend, I: IntoIterator, D: IntoIterator, { operation.op.insert_aux( insert.into_iter() .map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) .chain(delete.into_iter().map(|k| (k.to_vec(), None))) ) } /// Utility methods for the client. pub mod utils { use super::*; use crate::{backend::Backend, blockchain, error}; use primitives::H256; /// Returns a function for checking block ancestry, the returned function will /// return `true` if the given hash (second parameter) is a descendent of the /// base (first parameter). If the `current` parameter is defined, it should /// represent the current block `hash` and its `parent hash`, if given the /// function that's returned will assume that `hash` isn't part of the local DB /// yet, and all searches in the DB will instead reference the parent. pub fn is_descendent_of<'a, B, E, Block: BlockT, RA>( client: &'a Client, current: Option<(&'a H256, &'a H256)>, ) -> impl Fn(&H256, &H256) -> Result + 'a where B: Backend, E: CallExecutor + Send + Sync, { move |base, hash| { if base == hash { return Ok(false); } let mut hash = hash; if let Some((current_hash, current_parent_hash)) = current { if base == current_hash { return Ok(false); } if hash == current_hash { if base == current_parent_hash { return Ok(true); } else { hash = current_parent_hash; } } } let tree_route = blockchain::tree_route( |id| client.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), BlockId::Hash(*hash), BlockId::Hash(*base), )?; Ok(tree_route.common_block().hash == *base) } } } #[cfg(test)] pub(crate) mod tests { use std::collections::HashMap; use super::*; use primitives::blake2_256; use sr_primitives::DigestItem; use consensus::{BlockOrigin, SelectChain}; use test_client::{ prelude::*, client_db::{Backend, DatabaseSettings, PruningMode}, runtime::{self, Block, Transfer, RuntimeApi, TestAPI}, }; /// Returns tuple, consisting of: /// 1) test client pre-filled with blocks changing balances; /// 2) roots of changes tries for these blocks /// 3) test cases in form (begin, end, key, vec![(block, extrinsic)]) that are required to pass pub fn prepare_client_with_key_changes() -> ( test_client::client::Client, Vec, Vec<(u64, u64, Vec, Vec<(u64, u32)>)>, ) { // prepare block structure let blocks_transfers = vec![ vec![(AccountKeyring::Alice, AccountKeyring::Dave), (AccountKeyring::Bob, AccountKeyring::Dave)], vec![(AccountKeyring::Charlie, AccountKeyring::Eve)], vec![], vec![(AccountKeyring::Alice, AccountKeyring::Dave)], ]; // prepare client ang import blocks let mut local_roots = Vec::new(); let remote_client = TestClientBuilder::new().set_support_changes_trie(true).build(); let mut nonces: HashMap<_, u64> = Default::default(); for (i, block_transfers) in blocks_transfers.into_iter().enumerate() { let mut builder = remote_client.new_block(Default::default()).unwrap(); for (from, to) in block_transfers { builder.push_transfer(Transfer { from: from.into(), to: to.into(), amount: 1, nonce: *nonces.entry(from).and_modify(|n| { *n = *n + 1 }).or_default(), }).unwrap(); } remote_client.import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); let header = remote_client.header(&BlockId::Number(i as u64 + 1)).unwrap().unwrap(); let trie_root = header.digest().log(DigestItem::as_changes_trie_root) .map(|root| H256::from_slice(root.as_ref())) .unwrap(); local_roots.push(trie_root); } // prepare test cases let alice = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())).to_vec(); let bob = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Bob.into())).to_vec(); let charlie = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Charlie.into())).to_vec(); let dave = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec(); let eve = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Eve.into())).to_vec(); let ferdie = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Ferdie.into())).to_vec(); let test_cases = vec![ (1, 4, alice.clone(), vec![(4, 0), (1, 0)]), (1, 3, alice.clone(), vec![(1, 0)]), (2, 4, alice.clone(), vec![(4, 0)]), (2, 3, alice.clone(), vec![]), (1, 4, bob.clone(), vec![(1, 1)]), (1, 1, bob.clone(), vec![(1, 1)]), (2, 4, bob.clone(), vec![]), (1, 4, charlie.clone(), vec![(2, 0)]), (1, 4, dave.clone(), vec![(4, 0), (1, 1), (1, 0)]), (1, 1, dave.clone(), vec![(1, 1), (1, 0)]), (3, 4, dave.clone(), vec![(4, 0)]), (1, 4, eve.clone(), vec![(2, 0)]), (1, 1, eve.clone(), vec![]), (3, 4, eve.clone(), vec![]), (1, 4, ferdie.clone(), vec![]), ]; (remote_client, local_roots, test_cases) } #[test] fn client_initializes_from_genesis_ok() { let client = test_client::new(); assert_eq!( client.runtime_api().balance_of( &BlockId::Number(client.info().chain.best_number), AccountKeyring::Alice.into() ).unwrap(), 1000 ); assert_eq!( client.runtime_api().balance_of( &BlockId::Number(client.info().chain.best_number), AccountKeyring::Ferdie.into() ).unwrap(), 0 ); } #[test] fn block_builder_works_with_no_transactions() { let client = test_client::new(); let builder = client.new_block(Default::default()).unwrap(); client.import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); assert_eq!(client.info().chain.best_number, 1); } #[test] fn block_builder_works_with_transactions() { let client = test_client::new(); let mut builder = client.new_block(Default::default()).unwrap(); builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 42, nonce: 0, }).unwrap(); client.import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); assert_eq!(client.info().chain.best_number, 1); assert_ne!( client.state_at(&BlockId::Number(1)).unwrap().pairs(), client.state_at(&BlockId::Number(0)).unwrap().pairs() ); assert_eq!( client.runtime_api().balance_of( &BlockId::Number(client.info().chain.best_number), AccountKeyring::Alice.into() ).unwrap(), 958 ); assert_eq!( client.runtime_api().balance_of( &BlockId::Number(client.info().chain.best_number), AccountKeyring::Ferdie.into() ).unwrap(), 42 ); } #[test] fn block_builder_does_not_include_invalid() { let client = test_client::new(); let mut builder = client.new_block(Default::default()).unwrap(); builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 42, nonce: 0, }).unwrap(); assert!(builder.push_transfer(Transfer { from: AccountKeyring::Eve.into(), to: AccountKeyring::Alice.into(), amount: 42, nonce: 0, }).is_err()); client.import(BlockOrigin::Own, builder.bake().unwrap()).unwrap(); assert_eq!(client.info().chain.best_number, 1); assert_ne!( client.state_at(&BlockId::Number(1)).unwrap().pairs(), client.state_at(&BlockId::Number(0)).unwrap().pairs() ); assert_eq!(client.body(&BlockId::Number(1)).unwrap().unwrap().len(), 1) } #[test] fn best_containing_with_genesis_block() { // block tree: // G let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); let genesis_hash = client.info().chain.genesis_hash; assert_eq!( genesis_hash.clone(), longest_chain_select.finality_target(genesis_hash.clone(), None).unwrap().unwrap() ); } #[test] fn best_containing_with_hash_not_found() { // block tree: // G let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); let uninserted_block = client.new_block(Default::default()).unwrap().bake().unwrap(); assert_eq!( None, longest_chain_select.finality_target(uninserted_block.hash().clone(), None).unwrap() ); } #[test] fn uncles_with_only_ancestors() { // block tree: // G -> A1 -> A2 let client = test_client::new(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let v: Vec = Vec::new(); assert_eq!(v, client.uncles(a2.hash(), 3).unwrap()); } #[test] fn uncles_with_multiple_forks() { // block tree: // G -> A1 -> A2 -> A3 -> A4 -> A5 // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 let client = test_client::new(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); // A2 -> A3 let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a3.clone()).unwrap(); // A3 -> A4 let a4 = client.new_block_at(&BlockId::Hash(a3.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a4.clone()).unwrap(); // A4 -> A5 let a5 = client.new_block_at(&BlockId::Hash(a4.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a5.clone()).unwrap(); // A1 -> B2 let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 41, nonce: 0, }).unwrap(); let b2 = builder.bake().unwrap(); client.import(BlockOrigin::Own, b2.clone()).unwrap(); // B2 -> B3 let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b3.clone()).unwrap(); // B3 -> B4 let b4 = client.new_block_at(&BlockId::Hash(b3.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b4.clone()).unwrap(); // // B2 -> C3 let mut builder = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 1, }).unwrap(); let c3 = builder.bake().unwrap(); client.import(BlockOrigin::Own, c3.clone()).unwrap(); // A1 -> D2 let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 0, }).unwrap(); let d2 = builder.bake().unwrap(); client.import(BlockOrigin::Own, d2.clone()).unwrap(); let genesis_hash = client.info().chain.genesis_hash; let uncles1 = client.uncles(a4.hash(), 10).unwrap(); assert_eq!(vec![b2.hash(), d2.hash()], uncles1); let uncles2 = client.uncles(a4.hash(), 0).unwrap(); assert_eq!(0, uncles2.len()); let uncles3 = client.uncles(a1.hash(), 10).unwrap(); assert_eq!(0, uncles3.len()); let uncles4 = client.uncles(genesis_hash, 10).unwrap(); assert_eq!(0, uncles4.len()); let uncles5 = client.uncles(d2.hash(), 10).unwrap(); assert_eq!(vec![a2.hash(), b2.hash()], uncles5); let uncles6 = client.uncles(b3.hash(), 1).unwrap(); assert_eq!(vec![c3.hash()], uncles6); } #[test] fn best_containing_on_longest_chain_with_single_chain_3_blocks() { // block tree: // G -> A1 -> A2 let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let genesis_hash = client.info().chain.genesis_hash; assert_eq!(a2.hash(), longest_chain_select.finality_target(genesis_hash, None).unwrap().unwrap()); assert_eq!(a2.hash(), longest_chain_select.finality_target(a1.hash(), None).unwrap().unwrap()); assert_eq!(a2.hash(), longest_chain_select.finality_target(a2.hash(), None).unwrap().unwrap()); } #[test] fn best_containing_on_longest_chain_with_multiple_forks() { // block tree: // G -> A1 -> A2 -> A3 -> A4 -> A5 // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); // A2 -> A3 let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a3.clone()).unwrap(); // A3 -> A4 let a4 = client.new_block_at(&BlockId::Hash(a3.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a4.clone()).unwrap(); // A4 -> A5 let a5 = client.new_block_at(&BlockId::Hash(a4.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a5.clone()).unwrap(); // A1 -> B2 let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 41, nonce: 0, }).unwrap(); let b2 = builder.bake().unwrap(); client.import(BlockOrigin::Own, b2.clone()).unwrap(); // B2 -> B3 let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b3.clone()).unwrap(); // B3 -> B4 let b4 = client.new_block_at(&BlockId::Hash(b3.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b4.clone()).unwrap(); // // B2 -> C3 let mut builder = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 1, }).unwrap(); let c3 = builder.bake().unwrap(); client.import(BlockOrigin::Own, c3.clone()).unwrap(); // A1 -> D2 let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 0, }).unwrap(); let d2 = builder.bake().unwrap(); client.import(BlockOrigin::Own, d2.clone()).unwrap(); assert_eq!(client.info().chain.best_hash, a5.hash()); let genesis_hash = client.info().chain.genesis_hash; let leaves = longest_chain_select.leaves().unwrap(); assert!(leaves.contains(&a5.hash())); assert!(leaves.contains(&b4.hash())); assert!(leaves.contains(&c3.hash())); assert!(leaves.contains(&d2.hash())); assert_eq!(leaves.len(), 4); // search without restriction assert_eq!(a5.hash(), longest_chain_select.finality_target( genesis_hash, None).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a1.hash(), None).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a2.hash(), None).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a3.hash(), None).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a4.hash(), None).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a5.hash(), None).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b2.hash(), None).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b3.hash(), None).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b4.hash(), None).unwrap().unwrap()); assert_eq!(c3.hash(), longest_chain_select.finality_target( c3.hash(), None).unwrap().unwrap()); assert_eq!(d2.hash(), longest_chain_select.finality_target( d2.hash(), None).unwrap().unwrap()); // search only blocks with number <= 5. equivalent to without restriction for this scenario assert_eq!(a5.hash(), longest_chain_select.finality_target( genesis_hash, Some(5)).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a1.hash(), Some(5)).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a2.hash(), Some(5)).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a3.hash(), Some(5)).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a4.hash(), Some(5)).unwrap().unwrap()); assert_eq!(a5.hash(), longest_chain_select.finality_target( a5.hash(), Some(5)).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b2.hash(), Some(5)).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b3.hash(), Some(5)).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b4.hash(), Some(5)).unwrap().unwrap()); assert_eq!(c3.hash(), longest_chain_select.finality_target( c3.hash(), Some(5)).unwrap().unwrap()); assert_eq!(d2.hash(), longest_chain_select.finality_target( d2.hash(), Some(5)).unwrap().unwrap()); // search only blocks with number <= 4 assert_eq!(a4.hash(), longest_chain_select.finality_target( genesis_hash, Some(4)).unwrap().unwrap()); assert_eq!(a4.hash(), longest_chain_select.finality_target( a1.hash(), Some(4)).unwrap().unwrap()); assert_eq!(a4.hash(), longest_chain_select.finality_target( a2.hash(), Some(4)).unwrap().unwrap()); assert_eq!(a4.hash(), longest_chain_select.finality_target( a3.hash(), Some(4)).unwrap().unwrap()); assert_eq!(a4.hash(), longest_chain_select.finality_target( a4.hash(), Some(4)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( a5.hash(), Some(4)).unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b2.hash(), Some(4)).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b3.hash(), Some(4)).unwrap().unwrap()); assert_eq!(b4.hash(), longest_chain_select.finality_target( b4.hash(), Some(4)).unwrap().unwrap()); assert_eq!(c3.hash(), longest_chain_select.finality_target( c3.hash(), Some(4)).unwrap().unwrap()); assert_eq!(d2.hash(), longest_chain_select.finality_target( d2.hash(), Some(4)).unwrap().unwrap()); // search only blocks with number <= 3 assert_eq!(a3.hash(), longest_chain_select.finality_target( genesis_hash, Some(3)).unwrap().unwrap()); assert_eq!(a3.hash(), longest_chain_select.finality_target( a1.hash(), Some(3)).unwrap().unwrap()); assert_eq!(a3.hash(), longest_chain_select.finality_target( a2.hash(), Some(3)).unwrap().unwrap()); assert_eq!(a3.hash(), longest_chain_select.finality_target( a3.hash(), Some(3)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( a4.hash(), Some(3)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a5.hash(), Some(3)).unwrap()); assert_eq!(b3.hash(), longest_chain_select.finality_target( b2.hash(), Some(3)).unwrap().unwrap()); assert_eq!(b3.hash(), longest_chain_select.finality_target( b3.hash(), Some(3)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( b4.hash(), Some(3)).unwrap()); assert_eq!(c3.hash(), longest_chain_select.finality_target( c3.hash(), Some(3)).unwrap().unwrap()); assert_eq!(d2.hash(), longest_chain_select.finality_target( d2.hash(), Some(3)).unwrap().unwrap()); // search only blocks with number <= 2 assert_eq!(a2.hash(), longest_chain_select.finality_target( genesis_hash, Some(2)).unwrap().unwrap()); assert_eq!(a2.hash(), longest_chain_select.finality_target( a1.hash(), Some(2)).unwrap().unwrap()); assert_eq!(a2.hash(), longest_chain_select.finality_target( a2.hash(), Some(2)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( a3.hash(), Some(2)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a4.hash(), Some(2)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a5.hash(), Some(2)).unwrap()); assert_eq!(b2.hash(), longest_chain_select.finality_target( b2.hash(), Some(2)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( b3.hash(), Some(2)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b4.hash(), Some(2)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( c3.hash(), Some(2)).unwrap()); assert_eq!(d2.hash(), longest_chain_select.finality_target( d2.hash(), Some(2)).unwrap().unwrap()); // search only blocks with number <= 1 assert_eq!(a1.hash(), longest_chain_select.finality_target( genesis_hash, Some(1)).unwrap().unwrap()); assert_eq!(a1.hash(), longest_chain_select.finality_target( a1.hash(), Some(1)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( a2.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a3.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a4.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a5.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b2.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b3.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b4.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( c3.hash(), Some(1)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( d2.hash(), Some(1)).unwrap()); // search only blocks with number <= 0 assert_eq!(genesis_hash, longest_chain_select.finality_target( genesis_hash, Some(0)).unwrap().unwrap()); assert_eq!(None, longest_chain_select.finality_target( a1.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a2.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a3.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a4.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( a5.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b2.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b3.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( b4.hash(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( c3.hash().clone(), Some(0)).unwrap()); assert_eq!(None, longest_chain_select.finality_target( d2.hash().clone(), Some(0)).unwrap()); } #[test] fn best_containing_on_longest_chain_with_max_depth_higher_than_best() { // block tree: // G -> A1 -> A2 let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let genesis_hash = client.info().chain.genesis_hash; assert_eq!(a2.hash(), longest_chain_select.finality_target(genesis_hash, Some(10)).unwrap().unwrap()); } #[test] fn key_changes_works() { let (client, _, test_cases) = prepare_client_with_key_changes(); for (index, (begin, end, key, expected_result)) in test_cases.into_iter().enumerate() { let end = client.block_hash(end).unwrap().unwrap(); let actual_result = client.key_changes( begin, BlockId::Hash(end), None, &StorageKey(key), ).unwrap(); match actual_result == expected_result { true => (), false => panic!(format!("Failed test {}: actual = {:?}, expected = {:?}", index, actual_result, expected_result)), } } } #[test] fn import_with_justification() { let client = test_client::new(); // G -> A1 let a1 = client.new_block(Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); // A1 -> A2 let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); // A2 -> A3 let justification = vec![1, 2, 3]; let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap(); client.import_justified(BlockOrigin::Own, a3.clone(), justification.clone()).unwrap(); assert_eq!( client.info().chain.finalized_hash, a3.hash(), ); assert_eq!( client.justification(&BlockId::Hash(a3.hash())).unwrap(), Some(justification), ); assert_eq!( client.justification(&BlockId::Hash(a1.hash())).unwrap(), None, ); assert_eq!( client.justification(&BlockId::Hash(a2.hash())).unwrap(), None, ); } #[test] fn importing_diverged_finalized_block_should_trigger_reorg() { let client = test_client::new(); // G -> A1 -> A2 // \ // -> B1 let a1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 0, }).unwrap(); // create but don't import B1 just yet let b1 = b1.bake().unwrap(); // A2 is the current best since it's the longest chain assert_eq!( client.info().chain.best_hash, a2.hash(), ); // importing B1 as finalized should trigger a re-org and set it as new best let justification = vec![1, 2, 3]; client.import_justified(BlockOrigin::Own, b1.clone(), justification).unwrap(); assert_eq!( client.info().chain.best_hash, b1.hash(), ); assert_eq!( client.info().chain.finalized_hash, b1.hash(), ); } #[test] fn finalizing_diverged_block_should_trigger_reorg() { let (client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 -> A2 // \ // -> B1 -> B2 let a1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 0, }).unwrap(); let b1 = b1.bake().unwrap(); client.import(BlockOrigin::Own, b1.clone()).unwrap(); let b2 = client.new_block_at(&BlockId::Hash(b1.hash()), Default::default()).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b2.clone()).unwrap(); // A2 is the current best since it's the longest chain assert_eq!( client.info().chain.best_hash, a2.hash(), ); // we finalize block B1 which is on a different branch from current best // which should trigger a re-org. client.finalize_block(BlockId::Hash(b1.hash()), None).unwrap(); // B1 should now be the latest finalized assert_eq!( client.info().chain.finalized_hash, b1.hash(), ); // and B1 should be the new best block (`finalize_block` as no way of // knowing about B2) assert_eq!( client.info().chain.best_hash, b1.hash(), ); // `SelectChain` should report B2 as best block though assert_eq!( select_chain.best_chain().unwrap().hash(), b2.hash(), ); // after we build B3 on top of B2 and import it // it should be the new best block, let b3 = client.new_block_at( &BlockId::Hash(b2.hash()), Default::default(), ).unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b3.clone()).unwrap(); assert_eq!( client.info().chain.best_hash, b3.hash(), ); } #[test] fn get_header_by_block_number_doesnt_panic() { let client = test_client::new(); // backend uses u32 for block numbers, make sure we don't panic when // trying to convert let id = BlockId::::Number(72340207214430721); client.header(&id).expect_err("invalid block number overflows u32"); } #[test] fn state_reverted_on_reorg() { let _ = env_logger::try_init(); let client = test_client::new(); let current_balance = || client.runtime_api().balance_of( &BlockId::number(client.info().chain.best_number), AccountKeyring::Alice.into() ).unwrap(); // G -> A1 -> A2 // \ // -> B1 let mut a1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); a1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Bob.into(), amount: 10, nonce: 0, }).unwrap(); let a1 = a1.bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 50, nonce: 0, }).unwrap(); let b1 = b1.bake().unwrap(); // Reorg to B1 client.import_as_best(BlockOrigin::Own, b1.clone()).unwrap(); assert_eq!(950, current_balance()); let mut a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap(); a2.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Charlie.into(), amount: 10, nonce: 1, }).unwrap(); // Re-org to A2 client.import_as_best(BlockOrigin::Own, a2.bake().unwrap()).unwrap(); assert_eq!(980, current_balance()); } #[test] fn doesnt_import_blocks_that_revert_finality() { let _ = env_logger::try_init(); let tmp = tempfile::tempdir().unwrap(); // we need to run with archive pruning to avoid pruning non-canonical // states let backend = Arc::new(Backend::new( DatabaseSettings { cache_size: None, state_cache_size: 1 << 20, state_cache_child_ratio: None, path: tmp.path().into(), pruning: PruningMode::ArchiveAll, }, u64::max_value(), ).unwrap()); let client = TestClientBuilder::with_backend(backend).build(); // -> C1 // / // G -> A1 -> A2 // \ // -> B1 -> B2 -> B3 let a1 = client.new_block_at(&BlockId::Number(0), Default::default()) .unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a1.clone()).unwrap(); let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()) .unwrap().bake().unwrap(); client.import(BlockOrigin::Own, a2.clone()).unwrap(); let mut b1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 1, nonce: 0, }).unwrap(); let b1 = b1.bake().unwrap(); client.import(BlockOrigin::Own, b1.clone()).unwrap(); let b2 = client.new_block_at(&BlockId::Hash(b1.hash()), Default::default()) .unwrap().bake().unwrap(); client.import(BlockOrigin::Own, b2.clone()).unwrap(); // we will finalize A2 which should make it impossible to import a new // B3 at the same height but that doesnt't include it client.finalize_block(BlockId::Hash(a2.hash()), None).unwrap(); let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()) .unwrap().bake().unwrap(); let import_err = client.import(BlockOrigin::Own, b3).err().unwrap(); let expected_err = ConsensusError::ClientImport( error::Error::NotInFinalizedChain.to_string() ); assert_eq!( import_err.to_string(), expected_err.to_string(), ); // adding a C1 block which is lower than the last finalized should also // fail (with a cheaper check that doesn't require checking ancestry). let mut c1 = client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); // needed to make sure C1 gets a different hash from A1 and B1 c1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Ferdie.into(), amount: 2, nonce: 0, }).unwrap(); let c1 = c1.bake().unwrap(); let import_err = client.import(BlockOrigin::Own, c1).err().unwrap(); let expected_err = ConsensusError::ClientImport( error::Error::NotInFinalizedChain.to_string() ); assert_eq!( import_err.to_string(), expected_err.to_string(), ); } }