Move sc-client into sc-service (#5502)

* Drop client from sc-network and sc-client-db, move LongestChain to sc-client-api

* move leaves, cht, in_mem to sc-client-api, drop client from sc-finality-grandpa

* drop sc-service from sc-rpc

* drop sc-service from sc-consensus-aura

* drop sc-client from manual-seal and babe

* drop sc-client from utils/frame/rpc/system and utils/frame/benchmarking-cli

* drop sc-client from bin/node and bin/node-template

* drop sc-client

* fix tests

* remove check -p sc-client from gitlab.yml

* fix warnings

* fixes ui test

* fix light client tests

* adds associated Client type to AbstractService

* adds UsageProvider to Client

* fixed ui test, again

* tried and failed to get node-cli to compile for wasm

* thanks to tomaka for helping me get node-cli to compile for wasmm

* ui test pls pas 🙏🏾

* all tests passing 🪄

* no_run documentation code

* rm -f documentation code

* ClientProvider

* fix mega trait

* move LongestChain to sc-consensus, use adds minimal bounds to AbstractService::Client

* adds license to sc-consensus

Co-authored-by: Benjamin Kampmann <ben@parity.io>
This commit is contained in:
Seun Lanlege
2020-04-28 12:59:31 +01:00
committed by GitHub
parent 7784bdeffe
commit 4fa5941f44
87 changed files with 3937 additions and 3575 deletions
+70 -23
View File
@@ -20,14 +20,11 @@ use crate::status_sinks;
use crate::config::{Configuration, KeystoreConfig, PrometheusConfig, OffchainWorkerConfig};
use crate::metrics::MetricsService;
use sc_client_api::{
self,
BlockchainEvents,
backend::RemoteBackend, light::RemoteBlockchain,
execution_extensions::ExtensionsFactory,
ExecutorProvider, CallExecutor
self, BlockchainEvents, backend::RemoteBackend, light::RemoteBlockchain, execution_extensions::ExtensionsFactory,
ExecutorProvider, CallExecutor, ForkBlocks, BadBlocks, CloneableSpawn, UsageProvider,
};
use crate::client::{Client, ClientConfig};
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
use sc_client::{Client, ClientConfig};
use sc_chain_spec::get_extension;
use sp_consensus::{
block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator},
@@ -47,7 +44,7 @@ use sp_runtime::traits::{
Block as BlockT, NumberFor, SaturatedConversion, HashFor,
};
use sp_api::ProvideRuntimeApi;
use sc_executor::{NativeExecutor, NativeExecutionDispatch};
use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo};
use std::{
collections::HashMap,
io::{Read, Write, Seek},
@@ -57,7 +54,11 @@ use wasm_timer::SystemTime;
use sc_telemetry::{telemetry, SUBSTRATE_INFO};
use sp_transaction_pool::{MaintainedTransactionPool, ChainEvent};
use sp_blockchain;
use prometheus_endpoint::Registry as PrometheusRegistry;
use prometheus_endpoint::Registry;
use sc_client_db::{Backend, DatabaseSettings};
use sp_core::traits::CodeExecutor;
use sp_runtime::BuildStorage;
use sc_client_api::execution_extensions::ExecutionExtensions;
pub type BackgroundTask = Pin<Box<dyn Future<Output=()> + Send>>;
@@ -111,7 +112,7 @@ pub type TFullClient<TBl, TRtApi, TExecDisp> = Client<
pub type TFullBackend<TBl> = sc_client_db::Backend<TBl>;
/// Full client call executor type.
pub type TFullCallExecutor<TBl, TExecDisp> = sc_client::LocalCallExecutor<
pub type TFullCallExecutor<TBl, TExecDisp> = crate::client::LocalCallExecutor<
sc_client_db::Backend<TBl>,
NativeExecutor<TExecDisp>,
>;
@@ -125,19 +126,19 @@ pub type TLightClient<TBl, TRtApi, TExecDisp> = Client<
>;
/// Light client backend type.
pub type TLightBackend<TBl> = sc_client::light::backend::Backend<
pub type TLightBackend<TBl> = crate::client::light::backend::Backend<
sc_client_db::light::LightStorage<TBl>,
HashFor<TBl>,
>;
/// Light call executor type.
pub type TLightCallExecutor<TBl, TExecDisp> = sc_client::light::call_executor::GenesisCallExecutor<
sc_client::light::backend::Backend<
pub type TLightCallExecutor<TBl, TExecDisp> = crate::client::light::call_executor::GenesisCallExecutor<
crate::client::light::backend::Backend<
sc_client_db::light::LightStorage<TBl>,
HashFor<TBl>
>,
sc_client::LocalCallExecutor<
sc_client::light::backend::Backend<
crate::client::LocalCallExecutor<
crate::client::light::backend::Backend<
sc_client_db::light::LightStorage<TBl>,
HashFor<TBl>
>,
@@ -188,11 +189,11 @@ fn new_full_parts<TBl, TRtApi, TExecDisp>(
);
let chain_spec = &config.chain_spec;
let fork_blocks = get_extension::<sc_client::ForkBlocks<TBl>>(chain_spec.extensions())
let fork_blocks = get_extension::<ForkBlocks<TBl>>(chain_spec.extensions())
.cloned()
.unwrap_or_default();
let bad_blocks = get_extension::<sc_client::BadBlocks<TBl>>(chain_spec.extensions())
let bad_blocks = get_extension::<BadBlocks<TBl>>(chain_spec.extensions())
.cloned()
.unwrap_or_default();
@@ -210,7 +211,7 @@ fn new_full_parts<TBl, TRtApi, TExecDisp>(
Some(keystore.clone()),
);
sc_client_db::new_client(
new_client(
db_config,
executor,
chain_spec.as_storage_builder(),
@@ -229,6 +230,52 @@ fn new_full_parts<TBl, TRtApi, TExecDisp>(
Ok((client, backend, keystore, task_manager))
}
/// Create an instance of db-backed client.
pub fn new_client<E, Block, RA>(
settings: DatabaseSettings,
executor: E,
genesis_storage: &dyn BuildStorage,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
execution_extensions: ExecutionExtensions<Block>,
spawn_handle: Box<dyn CloneableSpawn>,
prometheus_registry: Option<Registry>,
config: ClientConfig,
) -> Result<(
crate::client::Client<
Backend<Block>,
crate::client::LocalCallExecutor<Backend<Block>, E>,
Block,
RA,
>,
Arc<Backend<Block>>,
),
sp_blockchain::Error,
>
where
Block: BlockT,
E: CodeExecutor + RuntimeInfo,
{
const CANONICALIZATION_DELAY: u64 = 4096;
let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = crate::client::LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone());
Ok((
crate::client::Client::new(
backend.clone(),
executor,
genesis_storage,
fork_blocks,
bad_blocks,
execution_extensions,
prometheus_registry,
config,
)?,
backend,
))
}
impl ServiceBuilder<(), (), (), (), (), (), (), (), (), (), ()> {
/// Start the service builder with a configuration.
pub fn new_full<TBl: BlockT, TRtApi, TExecDisp: NativeExecutionDispatch + 'static>(
@@ -315,18 +362,18 @@ impl ServiceBuilder<(), (), (), (), (), (), (), (), (), (), ()> {
};
sc_client_db::light::LightStorage::new(db_settings)?
};
let light_blockchain = sc_client::light::new_light_blockchain(db_storage);
let light_blockchain = crate::client::light::new_light_blockchain(db_storage);
let fetch_checker = Arc::new(
sc_client::light::new_fetch_checker::<_, TBl, _>(
crate::client::light::new_fetch_checker::<_, TBl, _>(
light_blockchain.clone(),
executor.clone(),
Box::new(task_manager.spawn_handle()),
),
);
let fetcher = Arc::new(sc_network::config::OnDemand::new(fetch_checker));
let backend = sc_client::light::new_light_backend(light_blockchain);
let backend = crate::client::light::new_light_backend(light_blockchain);
let remote_blockchain = backend.remote_blockchain();
let client = Arc::new(sc_client::light::new_light(
let client = Arc::new(crate::client::light::new_light(
backend.clone(),
config.chain_spec.as_storage_builder(),
executor,
@@ -601,7 +648,7 @@ impl<TBl, TRtApi, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TExPool, TRpc, Backend>
sc_transaction_pool::txpool::Options,
Arc<TCl>,
Option<TFchr>,
Option<&PrometheusRegistry>,
Option<&Registry>,
) -> Result<(UExPool, Option<BackgroundTask>), Error>
) -> Result<ServiceBuilder<TBl, TRtApi, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
UExPool, TRpc, Backend>, Error>
@@ -757,7 +804,7 @@ ServiceBuilder<
TBl: BlockT,
TRtApi: 'static + Send + Sync,
TBackend: 'static + sc_client_api::backend::Backend<TBl> + Send,
TExec: 'static + sc_client::CallExecutor<TBl> + Send + Sync + Clone,
TExec: 'static + CallExecutor<TBl> + Send + Sync + Clone,
TSc: Clone,
TImpQu: 'static + ImportQueue<TBl>,
TExPool: MaintainedTransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash> + MallocSizeOfWasm + 'static,
+1 -1
View File
@@ -27,7 +27,7 @@ use sp_runtime::traits::{
};
use sp_runtime::generic::{BlockId, SignedBlock};
use codec::{Decode, Encode, IoReader};
use sc_client::{Client, LocalCallExecutor};
use crate::client::{Client, LocalCallExecutor};
use sp_consensus::{
BlockOrigin,
import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult, ImportQueue},
@@ -0,0 +1,72 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Client fixed chain specification rules
use std::collections::{HashMap, HashSet};
use sp_runtime::{
traits::{Block as BlockT, NumberFor},
};
use sc_client_api::{ForkBlocks, BadBlocks};
/// Chain specification rules lookup result.
pub enum LookupResult<B: BlockT> {
/// Specification rules do not contain any special rules about this block
NotSpecial,
/// The bock is known to be bad and should not be imported
KnownBad,
/// There is a specified canonical block hash for the given height
Expected(B::Hash)
}
/// Chain-specific block filtering rules.
///
/// This holds known bad blocks and known good forks, and
/// is usually part of the chain spec.
pub struct BlockRules<B: BlockT> {
bad: HashSet<B::Hash>,
forks: HashMap<NumberFor<B>, B::Hash>,
}
impl<B: BlockT> BlockRules<B> {
/// New block rules with provided black and white lists.
pub fn new(
fork_blocks: ForkBlocks<B>,
bad_blocks: BadBlocks<B>,
) -> Self {
Self {
bad: bad_blocks.unwrap_or(HashSet::new()),
forks: fork_blocks.unwrap_or(vec![]).into_iter().collect(),
}
}
/// Check if there's any rule affecting the given block.
pub fn lookup(&self, number: NumberFor<B>, hash: &B::Hash) -> LookupResult<B> {
if let Some(hash_for_height) = self.forks.get(&number) {
if hash_for_height != hash {
return LookupResult::Expected(hash_for_height.clone());
}
}
if self.bad.contains(hash) {
return LookupResult::KnownBad;
}
LookupResult::NotSpecial
}
}
@@ -0,0 +1,276 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::{sync::Arc, panic::UnwindSafe, result, cell::RefCell};
use codec::{Encode, Decode};
use sp_runtime::{
generic::BlockId, traits::{Block as BlockT, HashFor, NumberFor},
};
use sp_state_machine::{
self, OverlayedChanges, Ext, ExecutionManager, StateMachine, ExecutionStrategy,
backend::Backend as _, StorageProof,
};
use sc_executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use sp_externalities::Extensions;
use sp_core::{NativeOrEncoded, NeverNativeValue, traits::CodeExecutor, offchain::storage::OffchainOverlayedChanges};
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
use sc_client_api::{backend, call_executor::CallExecutor, CloneableSpawn};
use super::client::ClientConfig;
/// Call executor that executes methods locally, querying all required
/// data from local backend.
pub struct LocalCallExecutor<B, E> {
backend: Arc<B>,
executor: E,
spawn_handle: Box<dyn CloneableSpawn>,
client_config: ClientConfig,
}
impl<B, E> LocalCallExecutor<B, E> {
/// Creates new instance of local call executor.
pub fn new(
backend: Arc<B>,
executor: E,
spawn_handle: Box<dyn CloneableSpawn>,
client_config: ClientConfig,
) -> Self {
LocalCallExecutor {
backend,
executor,
spawn_handle,
client_config,
}
}
}
impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
fn clone(&self) -> Self {
LocalCallExecutor {
backend: self.backend.clone(),
executor: self.executor.clone(),
spawn_handle: self.spawn_handle.clone(),
client_config: self.client_config.clone(),
}
}
}
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<B, E>
where
B: backend::Backend<Block>,
E: CodeExecutor + RuntimeInfo + Clone + 'static,
Block: BlockT,
{
type Error = E::Error;
type Backend = B;
fn call(
&self,
id: &BlockId<Block>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
extensions: Option<Extensions>,
) -> sp_blockchain::Result<Vec<u8>> {
let mut changes = OverlayedChanges::default();
let mut offchain_changes = if self.client_config.offchain_indexing_api {
OffchainOverlayedChanges::enabled()
} else {
OffchainOverlayedChanges::disabled()
};
let changes_trie = backend::changes_tries_state_at_block(
id, self.backend.changes_trie_storage()
)?;
let state = self.backend.state_at(*id)?;
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
let return_data = StateMachine::new(
&state,
changes_trie,
&mut changes,
&mut offchain_changes,
&self.executor,
method,
call_data,
extensions.unwrap_or_default(),
&state_runtime_code.runtime_code()?,
self.spawn_handle.clone(),
).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
strategy.get_manager(),
None,
)?;
Ok(return_data.into_encoded())
}
fn contextual_call<
'a,
IB: Fn() -> sp_blockchain::Result<()>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
Result<NativeOrEncoded<R>, Self::Error>
) -> Result<NativeOrEncoded<R>, Self::Error>,
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
>(
&self,
initialize_block_fn: IB,
at: &BlockId<Block>,
method: &str,
call_data: &[u8],
changes: &RefCell<OverlayedChanges>,
offchain_changes: &RefCell<OffchainOverlayedChanges>,
storage_transaction_cache: Option<&RefCell<
StorageTransactionCache<Block, B::State>
>>,
initialize_block: InitializeBlock<'a, Block>,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
recorder: &Option<ProofRecorder<Block>>,
extensions: Option<Extensions>,
) -> Result<NativeOrEncoded<R>, sp_blockchain::Error> where ExecutionManager<EM>: Clone {
match initialize_block {
InitializeBlock::Do(ref init_block)
if init_block.borrow().as_ref().map(|id| id != at).unwrap_or(true) => {
initialize_block_fn()?;
},
// We don't need to initialize the runtime at a block.
_ => {},
}
let changes_trie_state = backend::changes_tries_state_at_block(at, self.backend.changes_trie_storage())?;
let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut());
let mut state = self.backend.state_at(*at)?;
let changes = &mut *changes.borrow_mut();
let offchain_changes = &mut *offchain_changes.borrow_mut();
match recorder {
Some(recorder) => {
let trie_state = state.as_trie_backend()
.ok_or_else(||
Box::new(sp_state_machine::ExecutionError::UnableToGenerateProof) as Box<dyn sp_state_machine::Error>
)?;
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_state);
// It is important to extract the runtime code here before we create the proof
// recorder.
let runtime_code = state_runtime_code.runtime_code()?;
let backend = sp_state_machine::ProvingBackend::new_with_recorder(
trie_state,
recorder.clone(),
);
let mut state_machine = StateMachine::new(
&backend,
changes_trie_state,
changes,
offchain_changes,
&self.executor,
method,
call_data,
extensions.unwrap_or_default(),
&runtime_code,
self.spawn_handle.clone(),
);
// TODO: https://github.com/paritytech/substrate/issues/4455
// .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c))
state_machine.execute_using_consensus_failure_handler(execution_manager, native_call)
},
None => {
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
let runtime_code = state_runtime_code.runtime_code()?;
let mut state_machine = StateMachine::new(
&state,
changes_trie_state,
changes,
offchain_changes,
&self.executor,
method,
call_data,
extensions.unwrap_or_default(),
&runtime_code,
self.spawn_handle.clone(),
).with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c));
state_machine.execute_using_consensus_failure_handler(execution_manager, native_call)
}
}.map_err(Into::into)
}
fn runtime_version(&self, id: &BlockId<Block>) -> sp_blockchain::Result<RuntimeVersion> {
let mut overlay = OverlayedChanges::default();
let mut offchain_overlay = OffchainOverlayedChanges::default();
let changes_trie_state = backend::changes_tries_state_at_block(
id,
self.backend.changes_trie_storage(),
)?;
let state = self.backend.state_at(*id)?;
let mut cache = StorageTransactionCache::<Block, B::State>::default();
let mut ext = Ext::new(
&mut overlay,
&mut offchain_overlay,
&mut cache,
&state,
changes_trie_state,
None,
);
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
self.executor.runtime_version(&mut ext, &state_runtime_code.runtime_code()?)
.map_err(|e| sp_blockchain::Error::VersionInvalid(format!("{:?}", e)).into())
}
fn prove_at_trie_state<S: sp_state_machine::TrieBackendStorage<HashFor<Block>>>(
&self,
trie_state: &sp_state_machine::TrieBackend<S, HashFor<Block>>,
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, StorageProof), sp_blockchain::Error> {
sp_state_machine::prove_execution_on_trie_backend::<_, _, NumberFor<Block>, _>(
trie_state,
overlay,
&self.executor,
self.spawn_handle.clone(),
method,
call_data,
&sp_state_machine::backend::BackendRuntimeCode::new(trie_state).runtime_code()?,
)
.map_err(Into::into)
}
fn native_runtime_version(&self) -> Option<&NativeVersion> {
Some(self.executor.native_version())
}
}
impl<B, E, Block> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<B, E>
where
B: backend::Backend<Block>,
E: CodeExecutor + RuntimeInfo + Clone + 'static,
Block: BlockT,
{
fn native_version(&self) -> &sp_version::NativeVersion {
self.executor.native_version()
}
fn runtime_version(
&self,
at: &BlockId<Block>,
) -> Result<sp_version::RuntimeVersion, String> {
CallExecutor::runtime_version(self, at).map_err(|e| format!("{:?}", e))
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Tool for creating the genesis block.
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Hash as HashT, Zero};
/// Create a genesis block, given the initial storage.
pub fn construct_genesis_block<
Block: BlockT
> (
state_root: Block::Hash
) -> Block {
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
Vec::new(),
);
Block::new(
<<Block as BlockT>::Header as HeaderT>::new(
Zero::zero(),
extrinsics_root,
state_root,
Default::default(),
Default::default()
),
Default::default()
)
}
@@ -0,0 +1,515 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Light client backend. Only stores headers and justifications of blocks.
//! Everything else is requested from full nodes on demand.
use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;
use codec::{Decode, Encode};
use sp_core::ChangesTrieConfiguration;
use sp_core::storage::{well_known_keys, ChildInfo};
use sp_core::offchain::storage::InMemOffchainStorage;
use sp_state_machine::{
Backend as StateBackend, TrieBackend, InMemoryBackend, ChangesTrieTransaction,
StorageCollection, ChildStorageCollection,
};
use sp_runtime::{generic::BlockId, Justification, Storage};
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header, HashFor};
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sc_client_api::{
backend::{
AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState,
PrunableStateChangesTrieStorage,
},
blockchain::{
HeaderBackend as BlockchainHeaderBackend, well_known_cache_keys,
},
light::Storage as BlockchainStorage,
in_mem::check_genesis_storage,
UsageInfo,
};
use super::blockchain::Blockchain;
use hash_db::Hasher;
const IN_MEMORY_EXPECT_PROOF: &str = "InMemory state backend has Void error type and always succeeds; qed";
/// Light client backend.
pub struct Backend<S, H: Hasher> {
blockchain: Arc<Blockchain<S>>,
genesis_state: RwLock<Option<InMemoryBackend<H>>>,
import_lock: RwLock<()>,
}
/// Light block (header and justification) import operation.
pub struct ImportOperation<Block: BlockT, S> {
header: Option<Block::Header>,
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
leaf_state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
finalized_blocks: Vec<BlockId<Block>>,
set_head: Option<BlockId<Block>>,
storage_update: Option<InMemoryBackend<HashFor<Block>>>,
changes_trie_config_update: Option<Option<ChangesTrieConfiguration>>,
_phantom: std::marker::PhantomData<S>,
}
/// Either in-memory genesis state, or locally-unavailable state.
pub enum GenesisOrUnavailableState<H: Hasher> {
/// Genesis state - storage values are stored in-memory.
Genesis(InMemoryBackend<H>),
/// We know that state exists, but all calls will fail with error, because it
/// isn't locally available.
Unavailable,
}
impl<S, H: Hasher> Backend<S, H> {
/// Create new light backend.
pub fn new(blockchain: Arc<Blockchain<S>>) -> Self {
Self {
blockchain,
genesis_state: RwLock::new(None),
import_lock: Default::default(),
}
}
/// Get shared blockchain reference.
pub fn blockchain(&self) -> &Arc<Blockchain<S>> {
&self.blockchain
}
}
impl<S: AuxStore, H: Hasher> AuxStore for Backend<S, H> {
fn insert_aux<
'a,
'b: 'a,
'c: 'a,
I: IntoIterator<Item=&'a(&'c [u8], &'c [u8])>,
D: IntoIterator<Item=&'a &'b [u8]>,
>(&self, insert: I, delete: D) -> ClientResult<()> {
self.blockchain.storage().insert_aux(insert, delete)
}
fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
self.blockchain.storage().get_aux(key)
}
}
impl<S, Block> ClientBackend<Block> for Backend<S, HashFor<Block>>
where
Block: BlockT,
S: BlockchainStorage<Block>,
Block::Hash: Ord,
{
type BlockImportOperation = ImportOperation<Block, S>;
type Blockchain = Blockchain<S>;
type State = GenesisOrUnavailableState<HashFor<Block>>;
type OffchainStorage = InMemOffchainStorage;
fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
Ok(ImportOperation {
header: None,
cache: Default::default(),
leaf_state: NewBlockState::Normal,
aux_ops: Vec::new(),
finalized_blocks: Vec::new(),
set_head: None,
storage_update: None,
changes_trie_config_update: None,
_phantom: Default::default(),
})
}
fn begin_state_operation(
&self,
_operation: &mut Self::BlockImportOperation,
_block: BlockId<Block>
) -> ClientResult<()> {
Ok(())
}
fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> ClientResult<()> {
if !operation.finalized_blocks.is_empty() {
for block in operation.finalized_blocks {
self.blockchain.storage().finalize_header(block)?;
}
}
if let Some(header) = operation.header {
let is_genesis_import = header.number().is_zero();
if let Some(new_config) = operation.changes_trie_config_update {
operation.cache.insert(well_known_cache_keys::CHANGES_TRIE_CONFIG, new_config.encode());
}
self.blockchain.storage().import_header(
header,
operation.cache,
operation.leaf_state,
operation.aux_ops,
)?;
// when importing genesis block => remember its state
if is_genesis_import {
*self.genesis_state.write() = operation.storage_update.take();
}
} else {
for (key, maybe_val) in operation.aux_ops {
match maybe_val {
Some(val) => self.blockchain.storage().insert_aux(
&[(&key[..], &val[..])],
std::iter::empty(),
)?,
None => self.blockchain.storage().insert_aux(std::iter::empty(), &[&key[..]])?,
}
}
}
if let Some(set_head) = operation.set_head {
self.blockchain.storage().set_head(set_head)?;
}
Ok(())
}
fn finalize_block(
&self,
block: BlockId<Block>,
_justification: Option<Justification>,
) -> ClientResult<()> {
self.blockchain.storage().finalize_header(block)
}
fn blockchain(&self) -> &Blockchain<S> {
&self.blockchain
}
fn usage_info(&self) -> Option<UsageInfo> {
self.blockchain.storage().usage_info()
}
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>> {
None
}
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
None
}
fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
let block_number = self.blockchain.expect_block_number_from_id(&block)?;
// special case for genesis block
if block_number.is_zero() {
if let Some(genesis_state) = self.genesis_state.read().clone() {
return Ok(GenesisOrUnavailableState::Genesis(genesis_state));
}
}
// else return unavailable state. We do not return error here, because error
// would mean that we do not know this state at all. But we know that it exists
Ok(GenesisOrUnavailableState::Unavailable)
}
fn revert(
&self,
_n: NumberFor<Block>,
_revert_finalized: bool,
) -> ClientResult<NumberFor<Block>> {
Err(ClientError::NotAvailableOnLightClient)
}
fn get_import_lock(&self) -> &RwLock<()> {
&self.import_lock
}
}
impl<S, Block> RemoteBackend<Block> for Backend<S, HashFor<Block>>
where
Block: BlockT,
S: BlockchainStorage<Block> + 'static,
Block::Hash: Ord,
{
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
self.genesis_state.read().is_some()
&& self.blockchain.expect_block_number_from_id(block)
.map(|num| num.is_zero())
.unwrap_or(false)
}
fn remote_blockchain(&self) -> Arc<dyn super::blockchain::RemoteBlockchain<Block>> {
self.blockchain.clone()
}
}
impl<S, Block> BlockImportOperation<Block> for ImportOperation<Block, S>
where
Block: BlockT,
S: BlockchainStorage<Block>,
Block::Hash: Ord,
{
type State = GenesisOrUnavailableState<HashFor<Block>>;
fn state(&self) -> ClientResult<Option<&Self::State>> {
// None means 'locally-stateless' backend
Ok(None)
}
fn set_block_data(
&mut self,
header: Block::Header,
_body: Option<Vec<Block::Extrinsic>>,
_justification: Option<Justification>,
state: NewBlockState,
) -> ClientResult<()> {
self.leaf_state = state;
self.header = Some(header);
Ok(())
}
fn update_cache(&mut self, cache: HashMap<well_known_cache_keys::Id, Vec<u8>>) {
self.cache = cache;
}
fn update_db_storage(
&mut self,
_update: <Self::State as StateBackend<HashFor<Block>>>::Transaction,
) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn update_changes_trie(
&mut self,
_update: ChangesTrieTransaction<HashFor<Block>, NumberFor<Block>>,
) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn reset_storage(&mut self, input: Storage) -> ClientResult<Block::Hash> {
check_genesis_storage(&input)?;
// changes trie configuration
let changes_trie_config = input.top.iter()
.find(|(k, _)| &k[..] == well_known_keys::CHANGES_TRIE_CONFIG)
.map(|(_, v)| Decode::decode(&mut &v[..])
.expect("changes trie configuration is encoded properly at genesis"));
self.changes_trie_config_update = Some(changes_trie_config);
// this is only called when genesis block is imported => shouldn't be performance bottleneck
let mut storage: HashMap<Option<ChildInfo>, _> = HashMap::new();
storage.insert(None, input.top);
// create a list of children keys to re-compute roots for
let child_delta = input.children_default.iter()
.map(|(_storage_key, storage_child)| (storage_child.child_info.clone(), None))
.collect::<Vec<_>>();
// make sure to persist the child storage
for (_child_key, storage_child) in input.children_default {
storage.insert(Some(storage_child.child_info), storage_child.data);
}
let storage_update = InMemoryBackend::from(storage);
let (storage_root, _) = storage_update.full_storage_root(std::iter::empty(), child_delta);
self.storage_update = Some(storage_update);
Ok(storage_root)
}
fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
self.aux_ops.append(&mut ops.into_iter().collect());
Ok(())
}
fn update_storage(
&mut self,
_update: StorageCollection,
_child_update: ChildStorageCollection,
) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn mark_finalized(&mut self, block: BlockId<Block>, _justification: Option<Justification>) -> ClientResult<()> {
self.finalized_blocks.push(block);
Ok(())
}
fn mark_head(&mut self, block: BlockId<Block>) -> ClientResult<()> {
self.set_head = Some(block);
Ok(())
}
}
impl<H: Hasher> std::fmt::Debug for GenesisOrUnavailableState<H> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => state.fmt(f),
GenesisOrUnavailableState::Unavailable => write!(f, "Unavailable"),
}
}
}
impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
where
H::Out: Ord + codec::Codec,
{
type Error = ClientError;
type Transaction = <InMemoryBackend<H> as StateBackend<H>>::Transaction;
type TrieBackendStorage = <InMemoryBackend<H> as StateBackend<H>>::TrieBackendStorage;
fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
Ok(state.storage(key).expect(IN_MEMORY_EXPECT_PROOF)),
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
}
}
fn child_storage(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> ClientResult<Option<Vec<u8>>> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
Ok(state.child_storage(child_info, key).expect(IN_MEMORY_EXPECT_PROOF)),
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
}
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
Ok(state.next_storage_key(key).expect(IN_MEMORY_EXPECT_PROOF)),
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
}
}
fn next_child_storage_key(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<Vec<u8>>, Self::Error> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => Ok(
state.next_child_storage_key(child_info, key)
.expect(IN_MEMORY_EXPECT_PROOF)
),
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
}
}
fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, prefix: &[u8], action: A) {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => state.for_keys_with_prefix(prefix, action),
GenesisOrUnavailableState::Unavailable => (),
}
}
fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], action: A) {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => state.for_key_values_with_prefix(prefix, action),
GenesisOrUnavailableState::Unavailable => (),
}
}
fn for_keys_in_child_storage<A: FnMut(&[u8])>(
&self,
child_info: &ChildInfo,
action: A,
) {
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
state.for_keys_in_child_storage(child_info, action),
GenesisOrUnavailableState::Unavailable => (),
}
}
fn for_child_keys_with_prefix<A: FnMut(&[u8])>(
&self,
child_info: &ChildInfo,
prefix: &[u8],
action: A,
) {
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
state.for_child_keys_with_prefix(child_info, prefix, action),
GenesisOrUnavailableState::Unavailable => (),
}
}
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
match *self {
GenesisOrUnavailableState::Genesis(ref state) =>
state.storage_root(delta),
GenesisOrUnavailableState::Unavailable => Default::default(),
}
}
fn child_storage_root<I>(
&self,
child_info: &ChildInfo,
delta: I,
) -> (H::Out, bool, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
match *self {
GenesisOrUnavailableState::Genesis(ref state) => {
let (root, is_equal, _) = state.child_storage_root(child_info, delta);
(root, is_equal, Default::default())
},
GenesisOrUnavailableState::Unavailable =>
(H::Out::default(), true, Default::default()),
}
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => state.pairs(),
GenesisOrUnavailableState::Unavailable => Vec::new(),
}
}
fn keys(&self, prefix: &[u8]) -> Vec<Vec<u8>> {
match *self {
GenesisOrUnavailableState::Genesis(ref state) => state.keys(prefix),
GenesisOrUnavailableState::Unavailable => Vec::new(),
}
}
fn register_overlay_stats(&mut self, _stats: &sp_state_machine::StateMachineStats) { }
fn usage_info(&self) -> sp_state_machine::UsageInfo {
sp_state_machine::UsageInfo::empty()
}
fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
match self {
GenesisOrUnavailableState::Genesis(ref mut state) => state.as_trie_backend(),
GenesisOrUnavailableState::Unavailable => None,
}
}
}
@@ -0,0 +1,174 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Light client blockchain backend. Only stores headers and justifications of recent
//! blocks. CHT roots are stored for headers of ancient blocks.
use std::sync::Arc;
use sp_runtime::{Justification, generic::BlockId};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
use sp_blockchain::{
HeaderMetadata, CachedHeaderMetadata,
Error as ClientError, Result as ClientResult,
};
pub use sc_client_api::{
backend::{
AuxStore, NewBlockState
},
blockchain::{
Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache,
well_known_cache_keys,
},
light::{
RemoteBlockchain, LocalOrRemote, Storage
},
cht,
};
use super::fetcher::RemoteHeaderRequest;
/// Light client blockchain.
pub struct Blockchain<S> {
storage: S,
}
impl<S> Blockchain<S> {
/// Create new light blockchain backed with given storage.
pub fn new(storage: S) -> Self {
Self {
storage,
}
}
/// Get storage reference.
pub fn storage(&self) -> &S {
&self.storage
}
}
impl<S, Block> BlockchainHeaderBackend<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
match RemoteBlockchain::header(self, id)? {
LocalOrRemote::Local(header) => Ok(Some(header)),
LocalOrRemote::Remote(_) => Err(ClientError::NotAvailableOnLightClient),
LocalOrRemote::Unknown => Ok(None),
}
}
fn info(&self) -> BlockchainInfo<Block> {
self.storage.info()
}
fn status(&self, id: BlockId<Block>) -> ClientResult<BlockStatus> {
self.storage.status(id)
}
fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
self.storage.number(hash)
}
fn hash(&self, number: <<Block as BlockT>::Header as HeaderT>::Number) -> ClientResult<Option<Block::Hash>> {
self.storage.hash(number)
}
}
impl<S, Block> HeaderMetadata<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
type Error = ClientError;
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
self.storage.header_metadata(hash)
}
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
self.storage.insert_header_metadata(hash, metadata)
}
fn remove_header_metadata(&self, hash: Block::Hash) {
self.storage.remove_header_metadata(hash)
}
}
impl<S, Block> BlockchainBackend<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> {
fn body(&self, _id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
Err(ClientError::NotAvailableOnLightClient)
}
fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification>> {
Err(ClientError::NotAvailableOnLightClient)
}
fn last_finalized(&self) -> ClientResult<Block::Hash> {
self.storage.last_finalized()
}
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>> {
self.storage.cache()
}
fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
Err(ClientError::NotAvailableOnLightClient)
}
fn children(&self, _parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
Err(ClientError::NotAvailableOnLightClient)
}
}
impl<S: Storage<Block>, Block: BlockT> ProvideCache<Block> for Blockchain<S> {
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>> {
self.storage.cache()
}
}
impl<S, Block: BlockT> RemoteBlockchain<Block> for Blockchain<S>
where
S: Storage<Block>,
{
fn header(&self, id: BlockId<Block>) -> ClientResult<LocalOrRemote<
Block::Header,
RemoteHeaderRequest<Block::Header>,
>> {
// first, try to read header from local storage
if let Some(local_header) = self.storage.header(id)? {
return Ok(LocalOrRemote::Local(local_header));
}
// we need to know block number to check if it's a part of CHT
let number = match id {
BlockId::Hash(hash) => match self.storage.number(hash)? {
Some(number) => number,
None => return Ok(LocalOrRemote::Unknown),
},
BlockId::Number(number) => number,
};
// if the header is genesis (never pruned), non-canonical, or from future => return
if number.is_zero() || self.storage.status(BlockId::Number(number))? == BlockStatus::Unknown {
return Ok(LocalOrRemote::Unknown);
}
Ok(LocalOrRemote::Remote(RemoteHeaderRequest {
cht_root: match self.storage.header_cht_root(cht::size(), number)? {
Some(cht_root) => cht_root,
None => return Ok(LocalOrRemote::Unknown),
},
block: number,
retry_count: None,
}))
}
}
@@ -0,0 +1,297 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Methods that light client could use to execute runtime calls.
use std::{
sync::Arc, panic::UnwindSafe, result, cell::RefCell,
};
use codec::{Encode, Decode};
use sp_core::{convert_hash, NativeOrEncoded, traits::CodeExecutor, offchain::storage::OffchainOverlayedChanges};
use sp_runtime::{
generic::BlockId, traits::{One, Block as BlockT, Header as HeaderT, HashFor},
};
use sp_externalities::Extensions;
use sp_state_machine::{
self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend,
execution_proof_check_on_trie_backend, ExecutionManager, StorageProof, CloneableSpawn,
};
use hash_db::Hasher;
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sc_client_api::{
backend::RemoteBackend,
light::RemoteCallRequest,
call_executor::CallExecutor,
};
use sc_executor::{RuntimeVersion, NativeVersion};
/// Call executor that is able to execute calls only on genesis state.
///
/// Trying to execute call on non-genesis state leads to error.
pub struct GenesisCallExecutor<B, L> {
backend: Arc<B>,
local: L,
}
impl<B, L> GenesisCallExecutor<B, L> {
/// Create new genesis call executor.
pub fn new(backend: Arc<B>, local: L) -> Self {
Self { backend, local }
}
}
impl<B, L: Clone> Clone for GenesisCallExecutor<B, L> {
fn clone(&self) -> Self {
GenesisCallExecutor {
backend: self.backend.clone(),
local: self.local.clone(),
}
}
}
impl<Block, B, Local> CallExecutor<Block> for
GenesisCallExecutor<B, Local>
where
Block: BlockT,
B: RemoteBackend<Block>,
Local: CallExecutor<Block>,
{
type Error = ClientError;
type Backend = B;
fn call(
&self,
id: &BlockId<Block>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
extensions: Option<Extensions>,
) -> ClientResult<Vec<u8>> {
match self.backend.is_local_state_available(id) {
true => self.local.call(id, method, call_data, strategy, extensions),
false => Err(ClientError::NotAvailableOnLightClient),
}
}
fn contextual_call<
'a,
IB: Fn() -> ClientResult<()>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
Result<NativeOrEncoded<R>, Self::Error>
) -> Result<NativeOrEncoded<R>, Self::Error>,
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
>(
&self,
initialize_block_fn: IB,
at: &BlockId<Block>,
method: &str,
call_data: &[u8],
changes: &RefCell<OverlayedChanges>,
offchain_changes: &RefCell<OffchainOverlayedChanges>,
_: Option<&RefCell<StorageTransactionCache<Block, B::State>>>,
initialize_block: InitializeBlock<'a, Block>,
_manager: ExecutionManager<EM>,
native_call: Option<NC>,
recorder: &Option<ProofRecorder<Block>>,
extensions: Option<Extensions>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values
match self.backend.is_local_state_available(at) {
true => CallExecutor::contextual_call::<
_,
fn(
Result<NativeOrEncoded<R>, Local::Error>,
Result<NativeOrEncoded<R>, Local::Error>,
) -> Result<NativeOrEncoded<R>, Local::Error>,
_,
NC
>(
&self.local,
initialize_block_fn,
at,
method,
call_data,
changes,
offchain_changes,
None,
initialize_block,
ExecutionManager::NativeWhenPossible,
native_call,
recorder,
extensions,
).map_err(|e| ClientError::Execution(Box::new(e.to_string()))),
false => Err(ClientError::NotAvailableOnLightClient),
}
}
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
match self.backend.is_local_state_available(id) {
true => self.local.runtime_version(id),
false => Err(ClientError::NotAvailableOnLightClient),
}
}
fn prove_at_trie_state<S: sp_state_machine::TrieBackendStorage<HashFor<Block>>>(
&self,
_state: &sp_state_machine::TrieBackend<S, HashFor<Block>>,
_changes: &mut OverlayedChanges,
_method: &str,
_call_data: &[u8],
) -> ClientResult<(Vec<u8>, StorageProof)> {
Err(ClientError::NotAvailableOnLightClient)
}
fn native_runtime_version(&self) -> Option<&NativeVersion> {
None
}
}
/// Prove contextual execution using given block header in environment.
///
/// Method is executed using passed header as environment' current block.
/// Proof includes both environment preparation proof and method execution proof.
pub fn prove_execution<Block, S, E>(
mut state: S,
header: Block::Header,
executor: &E,
method: &str,
call_data: &[u8],
) -> ClientResult<(Vec<u8>, StorageProof)>
where
Block: BlockT,
S: StateBackend<HashFor<Block>>,
E: CallExecutor<Block>,
{
let trie_state = state.as_trie_backend()
.ok_or_else(||
Box::new(sp_state_machine::ExecutionError::UnableToGenerateProof) as
Box<dyn sp_state_machine::Error>
)?;
// prepare execution environment + record preparation proof
let mut changes = Default::default();
let (_, init_proof) = executor.prove_at_trie_state(
trie_state,
&mut changes,
"Core_initialize_block",
&header.encode(),
)?;
// execute method + record execution proof
let (result, exec_proof) = executor.prove_at_trie_state(
&trie_state,
&mut changes,
method,
call_data,
)?;
let total_proof = StorageProof::merge(vec![init_proof, exec_proof]);
Ok((result, total_proof))
}
/// Check remote contextual execution proof using given backend.
///
/// Method is executed using passed header as environment' current block.
/// Proof should include both environment preparation proof and method execution proof.
pub fn check_execution_proof<Header, E, H>(
executor: &E,
spawn_handle: Box<dyn CloneableSpawn>,
request: &RemoteCallRequest<Header>,
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>>
where
Header: HeaderT,
E: CodeExecutor + Clone + 'static,
H: Hasher,
H::Out: Ord + codec::Codec + 'static,
{
check_execution_proof_with_make_header::<Header, E, H, _>(
executor,
spawn_handle,
request,
remote_proof,
|header| <Header as HeaderT>::new(
*header.number() + One::one(),
Default::default(),
Default::default(),
header.hash(),
Default::default(),
),
)
}
/// Check remote contextual execution proof using given backend and header factory.
///
/// Method is executed using passed header as environment' current block.
/// Proof should include both environment preparation proof and method execution proof.
pub fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader>(
executor: &E,
spawn_handle: Box<dyn CloneableSpawn>,
request: &RemoteCallRequest<Header>,
remote_proof: StorageProof,
make_next_header: MakeNextHeader,
) -> ClientResult<Vec<u8>>
where
E: CodeExecutor + Clone + 'static,
H: Hasher,
Header: HeaderT,
H::Out: Ord + codec::Codec + 'static,
MakeNextHeader: Fn(&Header) -> Header,
{
let local_state_root = request.header.state_root();
let root: H::Out = convert_hash(&local_state_root);
// prepare execution environment + check preparation proof
let mut changes = OverlayedChanges::default();
let trie_backend = create_proof_check_backend(root, remote_proof)?;
let next_header = make_next_header(&request.header);
// TODO: Remove when solved: https://github.com/paritytech/substrate/issues/5047
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_backend);
let runtime_code = backend_runtime_code.runtime_code()?;
execution_proof_check_on_trie_backend::<H, Header::Number, _>(
&trie_backend,
&mut changes,
executor,
spawn_handle.clone(),
"Core_initialize_block",
&next_header.encode(),
&runtime_code,
)?;
// execute method
execution_proof_check_on_trie_backend::<H, Header::Number, _>(
&trie_backend,
&mut changes,
executor,
spawn_handle,
&request.method,
&request.call_data,
&runtime_code,
)
.map_err(Into::into)
}
@@ -0,0 +1,341 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Light client data fetcher. Fetches requested data from remote full nodes.
use std::sync::Arc;
use std::collections::{BTreeMap, HashMap};
use std::marker::PhantomData;
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
use codec::{Decode, Encode};
use sp_core::{convert_hash, traits::CodeExecutor};
use sp_core::storage::{ChildInfo, ChildType};
use sp_runtime::traits::{
Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor,
AtLeast32Bit, CheckedConversion,
};
use sp_state_machine::{
ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange,
InMemoryChangesTrieStorage, TrieBackend, read_proof_check, key_changes_proof_check_with_db,
read_child_proof_check, CloneableSpawn,
};
pub use sp_state_machine::StorageProof;
use sp_blockchain::{Error as ClientError, Result as ClientResult};
pub use sc_client_api::{
light::{
RemoteCallRequest, RemoteHeaderRequest, RemoteReadRequest, RemoteReadChildRequest,
RemoteChangesRequest, ChangesProof, RemoteBodyRequest, Fetcher, FetchChecker,
Storage as BlockchainStorage,
},
cht,
};
use super::blockchain::{Blockchain};
use super::call_executor::check_execution_proof;
/// Remote data checker.
pub struct LightDataChecker<E, H, B: BlockT, S: BlockchainStorage<B>> {
blockchain: Arc<Blockchain<S>>,
executor: E,
spawn_handle: Box<dyn CloneableSpawn>,
_hasher: PhantomData<(B, H)>,
}
impl<E, H, B: BlockT, S: BlockchainStorage<B>> LightDataChecker<E, H, B, S> {
/// Create new light data checker.
pub fn new(blockchain: Arc<Blockchain<S>>, executor: E, spawn_handle: Box<dyn CloneableSpawn>) -> Self {
Self {
blockchain, executor, spawn_handle, _hasher: PhantomData
}
}
/// Check remote changes query proof assuming that CHT-s are of given size.
pub fn check_changes_proof_with_cht_size(
&self,
request: &RemoteChangesRequest<B::Header>,
remote_proof: ChangesProof<B::Header>,
cht_size: NumberFor<B>,
) -> ClientResult<Vec<(NumberFor<B>, u32)>>
where
H: Hasher,
H::Out: Ord + codec::Codec,
{
// since we need roots of all changes tries for the range begin..max
// => remote node can't use max block greater that one that we have passed
if remote_proof.max_block > request.max_block.0 || remote_proof.max_block < request.last_block.0 {
return Err(ClientError::ChangesTrieAccessFailed(format!(
"Invalid max_block used by the remote node: {}. Local: {}..{}..{}",
remote_proof.max_block, request.first_block.0, request.last_block.0, request.max_block.0,
)).into());
}
// check if remote node has responded with extra changes trie roots proofs
// all changes tries roots must be in range [request.first_block.0; request.tries_roots.0)
let is_extra_first_root = remote_proof.roots.keys().next()
.map(|first_root| *first_root < request.first_block.0
|| *first_root >= request.tries_roots.0)
.unwrap_or(false);
let is_extra_last_root = remote_proof.roots.keys().next_back()
.map(|last_root| *last_root >= request.tries_roots.0)
.unwrap_or(false);
if is_extra_first_root || is_extra_last_root {
return Err(ClientError::ChangesTrieAccessFailed(format!(
"Extra changes tries roots proofs provided by the remote node: [{:?}..{:?}]. Expected in range: [{}; {})",
remote_proof.roots.keys().next(), remote_proof.roots.keys().next_back(),
request.first_block.0, request.tries_roots.0,
)).into());
}
// if request has been composed when some required headers were already pruned
// => remote node has sent us CHT-based proof of required changes tries roots
// => check that this proof is correct before proceeding with changes proof
let remote_max_block = remote_proof.max_block;
let remote_roots = remote_proof.roots;
let remote_roots_proof = remote_proof.roots_proof;
let remote_proof = remote_proof.proof;
if !remote_roots.is_empty() {
self.check_changes_tries_proof(
cht_size,
&remote_roots,
remote_roots_proof,
)?;
}
// and now check the key changes proof + get the changes
let mut result = Vec::new();
let proof_storage = InMemoryChangesTrieStorage::with_proof(remote_proof);
for config_range in &request.changes_trie_configs {
let result_range = key_changes_proof_check_with_db::<H, _>(
ChangesTrieConfigurationRange {
config: config_range.config.as_ref().ok_or(ClientError::ChangesTriesNotSupported)?,
zero: config_range.zero.0,
end: config_range.end.map(|(n, _)| n),
},
&RootsStorage {
roots: (request.tries_roots.0, &request.tries_roots.2),
prev_roots: &remote_roots,
},
&proof_storage,
request.first_block.0,
&ChangesTrieAnchorBlockId {
hash: convert_hash(&request.last_block.1),
number: request.last_block.0,
},
remote_max_block,
request.storage_key.as_ref(),
&request.key)
.map_err(|err| ClientError::ChangesTrieAccessFailed(err))?;
result.extend(result_range);
}
Ok(result)
}
/// Check CHT-based proof for changes tries roots.
pub fn check_changes_tries_proof(
&self,
cht_size: NumberFor<B>,
remote_roots: &BTreeMap<NumberFor<B>, B::Hash>,
remote_roots_proof: StorageProof,
) -> ClientResult<()>
where
H: Hasher,
H::Out: Ord + codec::Codec,
{
// all the checks are sharing the same storage
let storage = remote_roots_proof.into_memory_db();
// remote_roots.keys() are sorted => we can use this to group changes tries roots
// that are belongs to the same CHT
let blocks = remote_roots.keys().cloned();
cht::for_each_cht_group::<B::Header, _, _, _>(cht_size, blocks, |mut storage, _, cht_blocks| {
// get local changes trie CHT root for given CHT
// it should be there, because it is never pruned AND request has been composed
// when required header has been pruned (=> replaced with CHT)
let first_block = cht_blocks.first().cloned()
.expect("for_each_cht_group never calls callback with empty groups");
let local_cht_root = self.blockchain.storage().changes_trie_cht_root(cht_size, first_block)?
.ok_or(ClientError::InvalidCHTProof)?;
// check changes trie root for every block within CHT range
for block in cht_blocks {
// check if the proofs storage contains the root
// normally this happens in when the proving backend is created, but since
// we share the storage for multiple checks, do it here
let mut cht_root = H::Out::default();
cht_root.as_mut().copy_from_slice(local_cht_root.as_ref());
if !storage.contains(&cht_root, EMPTY_PREFIX) {
return Err(ClientError::InvalidCHTProof.into());
}
// check proof for single changes trie root
let proving_backend = TrieBackend::new(storage, cht_root);
let remote_changes_trie_root = remote_roots[&block];
cht::check_proof_on_proving_backend::<B::Header, H>(
local_cht_root,
block,
remote_changes_trie_root,
&proving_backend,
)?;
// and return the storage to use in following checks
storage = proving_backend.into_storage();
}
Ok(storage)
}, storage)
}
}
impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
where
Block: BlockT,
E: CodeExecutor + Clone + 'static,
H: Hasher,
H::Out: Ord + codec::Codec + 'static,
S: BlockchainStorage<Block>,
{
fn check_header_proof(
&self,
request: &RemoteHeaderRequest<Block::Header>,
remote_header: Option<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<Block::Header> {
let remote_header = remote_header.ok_or_else(||
ClientError::from(ClientError::InvalidCHTProof))?;
let remote_header_hash = remote_header.hash();
cht::check_proof::<Block::Header, H>(
request.cht_root,
request.block,
remote_header_hash,
remote_proof,
).map(|_| remote_header)
}
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
read_proof_check::<H, _>(
convert_hash(request.header.state_root()),
remote_proof,
request.keys.iter(),
).map_err(Into::into)
}
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
let child_info = match ChildType::from_prefixed_key(&request.storage_key) {
Some((ChildType::ParentKeyId, storage_key)) => ChildInfo::new_default(storage_key),
None => return Err("Invalid child type".into()),
};
read_child_proof_check::<H, _>(
convert_hash(request.header.state_root()),
remote_proof,
&child_info,
request.keys.iter(),
).map_err(Into::into)
}
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>> {
check_execution_proof::<_, _, H>(
&self.executor,
self.spawn_handle.clone(),
request,
remote_proof,
)
}
fn check_changes_proof(
&self,
request: &RemoteChangesRequest<Block::Header>,
remote_proof: ChangesProof<Block::Header>
) -> ClientResult<Vec<(NumberFor<Block>, u32)>> {
self.check_changes_proof_with_cht_size(request, remote_proof, cht::size())
}
fn check_body_proof(
&self,
request: &RemoteBodyRequest<Block::Header>,
body: Vec<Block::Extrinsic>
) -> ClientResult<Vec<Block::Extrinsic>> {
// TODO: #2621
let extrinsics_root = HashFor::<Block>::ordered_trie_root(
body.iter().map(Encode::encode).collect(),
);
if *request.header.extrinsics_root() == extrinsics_root {
Ok(body)
} else {
Err(format!("RemoteBodyRequest: invalid extrinsics root expected: {} but got {}",
*request.header.extrinsics_root(),
extrinsics_root,
).into())
}
}
}
/// A view of BTreeMap<Number, Hash> as a changes trie roots storage.
struct RootsStorage<'a, Number: AtLeast32Bit, Hash: 'a> {
roots: (Number, &'a [Hash]),
prev_roots: &'a BTreeMap<Number, Hash>,
}
impl<'a, H, Number, Hash> ChangesTrieRootsStorage<H, Number> for RootsStorage<'a, Number, Hash>
where
H: Hasher,
Number: std::fmt::Display + std::hash::Hash + Clone + AtLeast32Bit + Encode + Decode + Send + Sync + 'static,
Hash: 'a + Send + Sync + Clone + AsRef<[u8]>,
{
fn build_anchor(
&self,
_hash: H::Out,
) -> Result<sp_state_machine::ChangesTrieAnchorBlockId<H::Out, Number>, String> {
Err("build_anchor is only called when building block".into())
}
fn root(
&self,
_anchor: &ChangesTrieAnchorBlockId<H::Out, Number>,
block: Number,
) -> Result<Option<H::Out>, String> {
// we can't ask for roots from parallel forks here => ignore anchor
let root = if block < self.roots.0 {
self.prev_roots.get(&Number::unique_saturated_from(block)).cloned()
} else {
let index: Option<usize> = block.checked_sub(&self.roots.0).and_then(|index| index.checked_into());
match index {
Some(index) => self.roots.1.get(index as usize).cloned(),
None => None,
}
};
Ok(root.map(|root| {
let mut hasher_root: H::Out = Default::default();
hasher_root.as_mut().copy_from_slice(root.as_ref());
hasher_root
}))
}
}
@@ -0,0 +1,104 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Light client components.
pub mod backend;
pub mod blockchain;
pub mod call_executor;
pub mod fetcher;
use std::sync::Arc;
use sc_executor::RuntimeInfo;
use sp_core::traits::CodeExecutor;
use sp_runtime::BuildStorage;
use sp_runtime::traits::{Block as BlockT, HashFor};
use sp_blockchain::Result as ClientResult;
use prometheus_endpoint::Registry;
use super::call_executor::LocalCallExecutor;
use super::client::{Client,ClientConfig};
use sc_client_api::{
light::Storage as BlockchainStorage, CloneableSpawn,
};
use self::backend::Backend;
use self::blockchain::Blockchain;
use self::call_executor::GenesisCallExecutor;
use self::fetcher::LightDataChecker;
/// Create an instance of light client blockchain backend.
pub fn new_light_blockchain<B: BlockT, S: BlockchainStorage<B>>(storage: S) -> Arc<Blockchain<S>> {
Arc::new(Blockchain::new(storage))
}
/// Create an instance of light client backend.
pub fn new_light_backend<B, S>(blockchain: Arc<Blockchain<S>>) -> Arc<Backend<S, HashFor<B>>>
where
B: BlockT,
S: BlockchainStorage<B>,
{
Arc::new(Backend::new(blockchain))
}
/// Create an instance of light client.
pub fn new_light<B, S, RA, E>(
backend: Arc<Backend<S, HashFor<B>>>,
genesis_storage: &dyn BuildStorage,
code_executor: E,
spawn_handle: Box<dyn CloneableSpawn>,
prometheus_registry: Option<Registry>,
) -> ClientResult<
Client<
Backend<S, HashFor<B>>,
GenesisCallExecutor<
Backend<S, HashFor<B>>,
LocalCallExecutor<Backend<S, HashFor<B>>, E>
>,
B,
RA
>
>
where
B: BlockT,
S: BlockchainStorage<B> + 'static,
E: CodeExecutor + RuntimeInfo + Clone + 'static,
{
let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, spawn_handle.clone(), ClientConfig::default());
let executor = GenesisCallExecutor::new(backend.clone(), local_executor);
Client::new(
backend,
executor,
genesis_storage,
Default::default(),
Default::default(),
Default::default(),
prometheus_registry,
ClientConfig::default(),
)
}
/// Create an instance of fetch data checker.
pub fn new_fetch_checker<E, B: BlockT, S: BlockchainStorage<B>>(
blockchain: Arc<Blockchain<S>>,
executor: E,
spawn_handle: Box<dyn CloneableSpawn>,
) -> LightDataChecker<E, HashFor<B>, B, S>
where
E: CodeExecutor,
{
LightDataChecker::new(blockchain, executor, spawn_handle)
}
@@ -0,0 +1,54 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate Client and associated logic.
//!
//! The [`Client`] is one of the most important components of Substrate. It mainly comprises two
//! parts:
//!
//! - A database containing the blocks and chain state, generally referred to as
//! the [`Backend`](sc_client_api::backend::Backend).
//! - A runtime environment, generally referred to as the [`Executor`](CallExecutor).
//!
//! # Initialization
//!
//! Creating a [`Client`] is done by calling the `new` method and passing to it a
//! [`Backend`](sc_client_api::backend::Backend) and an [`Executor`](CallExecutor).
//!
//! The former is typically provided by the `sc-client-db` crate.
//!
//! The latter typically requires passing one of:
//!
//! - A [`LocalCallExecutor`] running the runtime locally.
//! - A [`RemoteCallExecutor`](light::call_executor::RemoteCallRequest) that will ask a
//! third-party to perform the executions.
//! - A [`RemoteOrLocalCallExecutor`](light::call_executor::RemoteOrLocalCallExecutor), combination
//! of the two.
//!
//! Additionally, the fourth generic parameter of the `Client` is a marker type representing
//! the ways in which the runtime can interface with the outside. Any code that builds a `Client`
//! is responsible for putting the right marker.
pub mod genesis;
pub mod light;
mod call_executor;
mod client;
mod block_rules;
pub use self::{
call_executor::LocalCallExecutor,
client::{new_with_backend, new_in_mem, Client, ClientConfig},
};
+1 -1
View File
@@ -16,11 +16,11 @@
//! Service configuration.
pub use sc_client::ExecutionStrategies;
pub use sc_client_db::{Database, PruningMode, DatabaseSettingsSrc as DatabaseConfig};
pub use sc_network::Multiaddr;
pub use sc_network::config::{ExtTransport, MultiaddrWithPeerId, NetworkConfiguration, Role, NodeKeyConfig};
pub use sc_executor::WasmExecutionMethod;
use sc_client_api::execution_extensions::ExecutionStrategies;
use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc};
pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions;
+118 -14
View File
@@ -18,6 +18,7 @@
//! Manages communication between them.
#![warn(missing_docs)]
#![recursion_limit="128"]
pub mod config;
#[macro_use]
@@ -26,6 +27,10 @@ pub mod error;
mod metrics;
mod builder;
#[cfg(feature = "test-helpers")]
pub mod client;
#[cfg(not(feature = "test-helpers"))]
mod client;
mod status_sinks;
mod task_manager;
@@ -38,7 +43,7 @@ use wasm_timer::Instant;
use std::task::{Poll, Context};
use parking_lot::Mutex;
use sc_client::Client;
use client::Client;
use futures::{
Future, FutureExt, Stream, StreamExt,
compat::*,
@@ -49,13 +54,13 @@ use sc_network::{NetworkService, network_state::NetworkState, PeerId, ReportHand
use log::{log, warn, debug, error, Level};
use codec::{Encode, Decode};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{NumberFor, Block as BlockT};
use sp_runtime::traits::{NumberFor, Block as BlockT, BlockIdTo};
use parity_util_mem::MallocSizeOf;
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
pub use self::error::Error;
pub use self::builder::{
new_full_client,
new_full_client, new_client,
ServiceBuilder, ServiceBuilderCommand, TFullClient, TLightClient, TFullBackend, TLightBackend,
TFullCallExecutor, TLightCallExecutor,
};
@@ -66,7 +71,6 @@ pub use sc_chain_spec::{
};
pub use sp_transaction_pool::{TransactionPool, InPoolTransaction, error::IntoPoolError};
pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions;
pub use sc_client::FinalityNotifications;
pub use sc_rpc::Metadata as RpcMetadata;
pub use sc_executor::NativeExecutionDispatch;
#[doc(hidden)]
@@ -76,6 +80,17 @@ pub use sc_network::config::{FinalityProofProvider, OnDemand, BoxFinalityProofRe
pub use sc_tracing::TracingReceiver;
pub use task_manager::SpawnTaskHandle;
use task_manager::TaskManager;
use sp_blockchain::{HeaderBackend, HeaderMetadata, ProvideCache};
use sp_api::{ProvideRuntimeApi, CallApiAt, ApiExt, ConstructRuntimeApi, ApiErrorExt};
use sc_client_api::{
LockImportRun, Backend as BackendT, ProofProvider, ProvideUncles,
StorageProvider, ExecutorProvider, Finalizer, AuxStore, Backend,
BlockBackend, BlockchainEvents, CallExecutor, TransactionFor,
UsageProvider,
};
use sc_block_builder::BlockBuilderProvider;
use sp_consensus::{block_validation::Chain, BlockImport};
use sp_block_builder::BlockBuilder;
const DEFAULT_PROTOCOL_ID: &str = "sup";
@@ -116,21 +131,104 @@ pub struct Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> {
impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Unpin for Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> {}
/// Client super trait, use this instead of the concrete Client type.
pub trait ClientProvider<
Block: BlockT,
Backend: BackendT<Block>,
Executor: CallExecutor<Block>,
Runtime: ConstructRuntimeApi<Block, Self>,
>:
HeaderBackend<Block>
+ ProvideRuntimeApi<
Block,
Api = <Runtime as ConstructRuntimeApi<Block, Self>>::RuntimeApi
>
+ LockImportRun<Block, Backend>
+ ProofProvider<Block>
+ BlockBuilderProvider<Backend, Block, Self>
+ ProvideUncles<Block>
+ StorageProvider<Block, Backend>
+ Chain<Block>
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
+ ExecutorProvider<Block, Executor = Executor>
+ ProvideCache<Block>
+ BlockIdTo<Block, Error = sp_blockchain::Error>
+ CallApiAt<
Block,
Error = sp_blockchain::Error,
StateBackend = <Backend as BackendT<Block>>::State
>
+ BlockImport<
Block,
Error = sp_consensus::Error,
Transaction = TransactionFor<Backend, Block>
>
+ Finalizer<Block, Backend>
+ BlockchainEvents<Block>
+ BlockBackend<Block>
+ UsageProvider<Block>
+ AuxStore
{}
impl<Block, Backend, Executor, Runtime> ClientProvider<Block, Backend, Executor, Runtime>
for
Client<Backend, Executor, Block, Runtime>
where
Block: BlockT,
Backend: BackendT<Block>,
Executor: CallExecutor<Block>,
Runtime: ConstructRuntimeApi<Block, Self>,
Self: HeaderBackend<Block>
+ ProvideRuntimeApi<
Block,
Api = <Runtime as ConstructRuntimeApi<Block, Self>>::RuntimeApi
>
+ LockImportRun<Block, Backend>
+ ProofProvider<Block>
+ BlockBuilderProvider<Backend, Block, Self>
+ ProvideUncles<Block>
+ StorageProvider<Block, Backend>
+ Chain<Block>
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
+ ExecutorProvider<Block, Executor = Executor>
+ ProvideCache<Block>
+ BlockIdTo<Block, Error = sp_blockchain::Error>
+ CallApiAt<
Block,
Error = sp_blockchain::Error,
StateBackend = <Backend as BackendT<Block>>::State
>
+ BlockImport<
Block,
Error = sp_consensus::Error,
Transaction = TransactionFor<Backend, Block>
>
+ Finalizer<Block, Backend>
+ BlockchainEvents<Block>
+ BlockBackend<Block>
+ UsageProvider<Block>
+ AuxStore
{}
/// Abstraction over a Substrate service.
pub trait AbstractService: 'static + Future<Output = Result<(), Error>> +
Spawn + Send + Unpin {
pub trait AbstractService: Future<Output = Result<(), Error>> + Send + Unpin + Spawn + 'static {
/// Type of block of this chain.
type Block: BlockT;
/// Backend storage for the client.
type Backend: 'static + sc_client_api::backend::Backend<Self::Block>;
type Backend: 'static + BackendT<Self::Block>;
/// How to execute calls towards the runtime.
type CallExecutor: 'static + sc_client::CallExecutor<Self::Block> + Send + Sync + Clone;
type CallExecutor: 'static + CallExecutor<Self::Block> + Send + Sync + Clone;
/// API that the runtime provides.
type RuntimeApi: Send + Sync;
/// Chain selection algorithm.
type SelectChain: sp_consensus::SelectChain<Self::Block>;
/// Transaction pool.
type TransactionPool: TransactionPool<Block = Self::Block> + MallocSizeOfWasm;
/// The generic Client type, the bounds here are the ones specifically required by
/// internal crates like sc_informant.
type Client:
HeaderMetadata<Self::Block, Error = sp_blockchain::Error> + UsageProvider<Self::Block>
+ BlockchainEvents<Self::Block> + HeaderBackend<Self::Block> + Send + Sync;
/// Get event stream for telemetry connection established events.
fn telemetry_on_connect_stream(&self) -> TracingUnboundedReceiver<()>;
@@ -170,7 +268,7 @@ pub trait AbstractService: 'static + Future<Output = Result<(), Error>> +
fn rpc_query(&self, mem: &RpcSession, request: &str) -> Pin<Box<dyn Future<Output = Option<String>> + Send>>;
/// Get shared client instance.
fn client(&self) -> Arc<sc_client::Client<Self::Backend, Self::CallExecutor, Self::Block, Self::RuntimeApi>>;
fn client(&self) -> Arc<Self::Client>;
/// Get clone of select chain.
fn select_chain(&self) -> Option<Self::SelectChain>;
@@ -198,9 +296,14 @@ impl<TBl, TBackend, TExec, TRtApi, TSc, TExPool, TOc> AbstractService for
NetworkService<TBl, TBl::Hash>, TExPool, TOc>
where
TBl: BlockT,
TBackend: 'static + sc_client_api::backend::Backend<TBl>,
TExec: 'static + sc_client::CallExecutor<TBl> + Send + Sync + Clone,
TRtApi: 'static + Send + Sync,
TBackend: 'static + Backend<TBl>,
TExec: 'static + CallExecutor<TBl, Backend = TBackend> + Send + Sync + Clone,
TRtApi: 'static + Send + Sync + ConstructRuntimeApi<TBl, Client<TBackend, TExec, TBl, TRtApi>>,
<TRtApi as ConstructRuntimeApi<TBl, Client<TBackend, TExec, TBl, TRtApi>>>::RuntimeApi:
sp_api::Core<TBl>
+ ApiExt<TBl, StateBackend = TBackend::State>
+ ApiErrorExt<Error = sp_blockchain::Error>
+ BlockBuilder<TBl>,
TSc: sp_consensus::SelectChain<TBl> + 'static + Clone + Send + Unpin,
TExPool: 'static + TransactionPool<Block = TBl> + MallocSizeOfWasm,
TOc: 'static + Send + Sync,
@@ -211,6 +314,7 @@ where
type RuntimeApi = TRtApi;
type SelectChain = TSc;
type TransactionPool = TExPool;
type Client = Client<Self::Backend, Self::CallExecutor, Self::Block, Self::RuntimeApi>;
fn telemetry_on_connect_stream(&self) -> TracingUnboundedReceiver<()> {
let (sink, stream) = tracing_unbounded("mpsc_telemetry_on_connect");
@@ -254,7 +358,7 @@ where
)
}
fn client(&self) -> Arc<sc_client::Client<Self::Backend, Self::CallExecutor, Self::Block, Self::RuntimeApi>> {
fn client(&self) -> Arc<Self::Client> {
self.client.clone()
}
@@ -326,7 +430,7 @@ impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Spawn for
/// The `status_sink` contain a list of senders to send a periodic network status to.
fn build_network_future<
B: BlockT,
C: sc_client::BlockchainEvents<B>,
C: BlockchainEvents<B>,
H: sc_network::ExHashT
> (
role: Role,
+1 -1
View File
@@ -18,11 +18,11 @@ use std::convert::TryFrom;
use crate::NetworkStatus;
use prometheus_endpoint::{register, Gauge, U64, F64, Registry, PrometheusError, Opts, GaugeVec};
use sc_client::ClientInfo;
use sc_telemetry::{telemetry, SUBSTRATE_INFO};
use sp_runtime::traits::{NumberFor, Block, SaturatedConversion, UniqueSaturatedInto};
use sp_transaction_pool::PoolStatus;
use sp_utils::metrics::register_globals;
use sc_client_api::ClientInfo;
use sysinfo::{self, ProcessExt, SystemExt};