Phase 1 of repo reorg (#719)

* Remove unneeded script

* Rename Substrate Demo -> Substrate

* Rename demo -> node

* Build wasm from last rename.

* Merge ed25519 into substrate-primitives

* Minor tweak

* Rename substrate -> core

* Move substrate-runtime-support to core/runtime/support

* Rename/move substrate-runtime-version

* Move codec up a level

* Rename substrate-codec -> parity-codec

* Move environmental up a level

* Move pwasm-* up to top, ready for removal

* Remove requirement of s-r-support from s-r-primitives

* Move core/runtime/primitives into core/runtime-primitives

* Remove s-r-support dep from s-r-version

* Remove dep of s-r-support from bft

* Remove dep of s-r-support from node/consensus

* Sever all other core deps from s-r-support

* Forgot the no_std directive

* Rename non-SRML modules to sr-* to avoid match clashes

* Move runtime/* to srml/*

* Rename substrate-runtime-* -> srml-*

* Move srml to top-level
This commit is contained in:
Gav Wood
2018-09-12 11:13:31 +02:00
committed by Arkadiy Paronyan
parent 8fe5aa4c81
commit 1e01162505
374 changed files with 2845 additions and 2902 deletions
+229
View File
@@ -0,0 +1,229 @@
// Copyright 2017 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::sync::{Arc, Weak};
use futures::{Future, IntoFuture};
use parking_lot::RwLock;
use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::BlockId};
use runtime_primitives::traits::{Block as BlockT, NumberFor};
use state_machine::{
Backend as StateBackend,
TrieBackend as StateTrieBackend,
TryIntoTrieBackend as TryIntoStateTrieBackend
};
use backend::{Backend as ClientBackend, BlockImportOperation, RemoteBackend};
use blockchain::HeaderBackend as BlockchainHeaderBackend;
use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
use light::blockchain::{Blockchain, Storage as BlockchainStorage};
use light::fetcher::{Fetcher, RemoteReadRequest};
use patricia_trie::NodeCodec;
use hashdb::Hasher;
/// Light client backend.
pub struct Backend<S, F> {
blockchain: Arc<Blockchain<S, F>>,
}
/// Light block (header and justification) import operation.
pub struct ImportOperation<Block: BlockT, S, F> {
is_new_best: bool,
header: Option<Block::Header>,
authorities: Option<Vec<AuthorityId>>,
_phantom: ::std::marker::PhantomData<(S, F)>,
}
/// On-demand state.
pub struct OnDemandState<Block: BlockT, S, F> {
fetcher: Weak<F>,
blockchain: Weak<Blockchain<S, F>>,
block: Block::Hash,
cached_header: RwLock<Option<Block::Header>>,
}
impl<S, F> Backend<S, F> {
/// Create new light backend.
pub fn new(blockchain: Arc<Blockchain<S, F>>) -> Self {
Self { blockchain }
}
/// Get shared blockchain reference.
pub fn blockchain(&self) -> &Arc<Blockchain<S, F>> {
&self.blockchain
}
}
impl<S, F, Block, H, C> ClientBackend<Block, H, C> for Backend<S, F> where
Block: BlockT,
S: BlockchainStorage<Block>,
F: Fetcher<Block>,
H: Hasher,
C: NodeCodec<H>,
{
type BlockImportOperation = ImportOperation<Block, S, F>;
type Blockchain = Blockchain<S, F>;
type State = OnDemandState<Block, S, F>;
fn begin_operation(&self, _block: BlockId<Block>) -> ClientResult<Self::BlockImportOperation> {
Ok(ImportOperation {
is_new_best: false,
header: None,
authorities: None,
_phantom: Default::default(),
})
}
fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> {
let header = operation.header.expect("commit is called after set_block_data; set_block_data sets header; qed");
self.blockchain.storage().import_header(operation.is_new_best, header, operation.authorities)
}
fn blockchain(&self) -> &Blockchain<S, F> {
&self.blockchain
}
fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
let block_hash = match block {
BlockId::Hash(h) => Some(h),
BlockId::Number(n) => self.blockchain.hash(n).unwrap_or_default(),
};
Ok(OnDemandState {
fetcher: self.blockchain.fetcher(),
blockchain: Arc::downgrade(&self.blockchain),
block: block_hash.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", block)))?,
cached_header: RwLock::new(None),
})
}
fn revert(&self, _n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
unimplemented!()
}
}
impl<S, F, Block, H, C> RemoteBackend<Block, H, C> for Backend<S, F>
where
Block: BlockT,
S: BlockchainStorage<Block>,
F: Fetcher<Block>,
H: Hasher,
C: NodeCodec<H>,
{}
impl<S, F, Block, H, C> BlockImportOperation<Block, H, C> for ImportOperation<Block, S, F>
where
Block: BlockT,
F: Fetcher<Block>,
S: BlockchainStorage<Block>,
H: Hasher,
C: NodeCodec<H>,
{
type State = OnDemandState<Block, S, F>;
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<Block::Hash>>,
is_new_best: bool
) -> ClientResult<()> {
self.is_new_best = is_new_best;
self.header = Some(header);
Ok(())
}
fn update_authorities(&mut self, authorities: Vec<AuthorityId>) {
self.authorities = Some(authorities);
}
fn update_storage(&mut self, _update: <Self::State as StateBackend<H, C>>::Transaction) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
fn reset_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, _iter: I) -> ClientResult<()> {
// we're not storing anything locally => ignore changes
Ok(())
}
}
impl<Block, S, F, H, C> StateBackend<H, C> for OnDemandState<Block, S, F>
where
Block: BlockT,
S: BlockchainStorage<Block>,
F: Fetcher<Block>,
H: Hasher,
C: NodeCodec<H>,
{
type Error = ClientError;
type Transaction = ();
fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
let mut header = self.cached_header.read().clone();
if header.is_none() {
let cached_header = self.blockchain.upgrade()
.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", self.block)).into())
.and_then(|blockchain| blockchain.expect_header(BlockId::Hash(self.block)))?;
header = Some(cached_header.clone());
*self.cached_header.write() = Some(cached_header);
}
self.fetcher.upgrade().ok_or(ClientErrorKind::NotAvailableOnLightClient)?
.remote_read(RemoteReadRequest {
block: self.block,
header: header.expect("if block above guarantees that header is_some(); qed"),
key: key.to_vec(),
retry_count: None,
})
.into_future().wait()
}
fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, _prefix: &[u8], _action: A) {
// whole state is not available on light node
}
fn storage_root<I>(&self, _delta: I) -> (H::Out, Self::Transaction)
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
(H::Out::default(), ())
}
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
// whole state is not available on light node
Vec::new()
}
}
impl<Block, S, F, H, C> TryIntoStateTrieBackend<H, C> for OnDemandState<Block, S, F>
where
Block: BlockT,
F: Fetcher<Block>,
H: Hasher,
C: NodeCodec<H>,
{
fn try_into_trie_backend(self) -> Option<StateTrieBackend<H, C>> {
None
}
}
@@ -0,0 +1,142 @@
// Copyright 2017 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 blockchin backend. Only stores headers and justifications of recent
//! blocks. CHT roots are stored for headers of ancient blocks.
use std::sync::Weak;
use futures::{Future, IntoFuture};
use parking_lot::Mutex;
use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::BlockId};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
use blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo};
use cht;
use error::{ErrorKind as ClientErrorKind, Result as ClientResult};
use light::fetcher::{Fetcher, RemoteHeaderRequest};
/// Light client blockchain storage.
pub trait Storage<Block: BlockT>: BlockchainHeaderBackend<Block> {
/// Store new header.
fn import_header(
&self,
is_new_best: bool,
header: Block::Header,
authorities: Option<Vec<AuthorityId>>
) -> ClientResult<()>;
/// Get CHT root for given block. Fails if the block is not pruned (not a part of any CHT).
fn cht_root(&self, cht_size: u64, block: NumberFor<Block>) -> ClientResult<Block::Hash>;
/// Get storage cache.
fn cache(&self) -> Option<&BlockchainCache<Block>>;
}
/// Light client blockchain.
pub struct Blockchain<S, F> {
fetcher: Mutex<Weak<F>>,
storage: S,
}
impl<S, F> Blockchain<S, F> {
/// Create new light blockchain backed with given storage.
pub fn new(storage: S) -> Self {
Self {
fetcher: Mutex::new(Default::default()),
storage,
}
}
/// Sets fetcher reference.
pub fn set_fetcher(&self, fetcher: Weak<F>) {
*self.fetcher.lock() = fetcher;
}
/// Get fetcher weak reference.
pub fn fetcher(&self) -> Weak<F> {
self.fetcher.lock().clone()
}
/// Get storage reference.
pub fn storage(&self) -> &S {
&self.storage
}
}
impl<S, F, Block> BlockchainHeaderBackend<Block> for Blockchain<S, F> where Block: BlockT, S: Storage<Block>, F: Fetcher<Block> {
fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
match self.storage.header(id)? {
Some(header) => Ok(Some(header)),
None => {
let number = match id {
BlockId::Hash(hash) => match self.storage.number(hash)? {
Some(number) => number,
None => return Ok(None),
},
BlockId::Number(number) => number,
};
// if the header is from future or genesis (we never prune genesis) => return
if number.is_zero() || self.storage.status(BlockId::Number(number))? != BlockStatus::InChain {
return Ok(None);
}
self.fetcher().upgrade().ok_or(ClientErrorKind::NotAvailableOnLightClient)?
.remote_header(RemoteHeaderRequest {
cht_root: self.storage.cht_root(cht::SIZE, number)?,
block: number,
retry_count: None,
})
.into_future().wait()
.map(Some)
}
}
}
fn info(&self) -> ClientResult<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, F, Block> BlockchainBackend<Block> for Blockchain<S, F> where Block: BlockT, S: Storage<Block>, F: Fetcher<Block> {
fn body(&self, _id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
// TODO [light]: fetch from remote node
Ok(None)
}
fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification<Block::Hash>>> {
Ok(None)
}
fn cache(&self) -> Option<&BlockchainCache<Block>> {
self.storage.cache()
}
}
@@ -0,0 +1,191 @@
// Copyright 2017 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 call exector. Executes methods on remote full nodes, fetching
//! execution proof and checking it locally.
use std::sync::Arc;
use futures::{IntoFuture, Future};
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges,
execution_proof_check, ExecutionManager};
use primitives::H256;
use patricia_trie::NodeCodec;
use hashdb::Hasher;
use rlp::Encodable;
use blockchain::Backend as ChainBackend;
use call_executor::{CallExecutor, CallResult};
use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
use light::fetcher::{Fetcher, RemoteCallRequest};
use executor::RuntimeVersion;
use codec::Decode;
use heapsize::HeapSizeOf;
use std::marker::PhantomData;
/// Call executor that executes methods on remote node, querying execution proof
/// and checking proof by re-executing locally.
pub struct RemoteCallExecutor<B, F, H, C> {
blockchain: Arc<B>,
fetcher: Arc<F>,
_hasher: PhantomData<H>,
_codec: PhantomData<C>,
}
impl<B, F, H, C> Clone for RemoteCallExecutor<B, F, H, C> {
fn clone(&self) -> Self {
RemoteCallExecutor {
blockchain: self.blockchain.clone(),
fetcher: self.fetcher.clone(),
_hasher: Default::default(),
_codec: Default::default(),
}
}
}
impl<B, F, H, C> RemoteCallExecutor<B, F, H, C> {
/// Creates new instance of remote call executor.
pub fn new(blockchain: Arc<B>, fetcher: Arc<F>) -> Self {
RemoteCallExecutor { blockchain, fetcher, _hasher: PhantomData, _codec: PhantomData }
}
}
impl<B, F, Block, H, C> CallExecutor<Block, H, C> for RemoteCallExecutor<B, F, H, C>
where
Block: BlockT,
B: ChainBackend<Block>,
F: Fetcher<Block>,
H: Hasher,
H::Out: Ord + Encodable,
C: NodeCodec<H>
{
type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<CallResult> {
let block_hash = match *id {
BlockId::Hash(hash) => hash,
BlockId::Number(number) => self.blockchain.hash(number)?
.ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", number)))?,
};
let block_header = self.blockchain.expect_header(id.clone())?;
self.fetcher.remote_call(RemoteCallRequest {
block: block_hash,
header: block_header,
method: method.into(),
call_data: call_data.to_vec(),
retry_count: None,
}).into_future().wait()
}
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
let call_result = self.call(id, "version", &[])?;
RuntimeVersion::decode(&mut call_result.return_data.as_slice())
.ok_or_else(|| ClientErrorKind::VersionInvalid.into())
}
fn call_at_state<
S: StateBackend<H, C>,
FF: FnOnce(Result<Vec<u8>, Self::Error>, Result<Vec<u8>, Self::Error>) -> Result<Vec<u8>, Self::Error>
>(&self,
_state: &S,
_changes: &mut OverlayedChanges,
_method: &str,
_call_data: &[u8],
_m: ExecutionManager<FF>
) -> ClientResult<(Vec<u8>, S::Transaction)> {
Err(ClientErrorKind::NotAvailableOnLightClient.into())
}
fn prove_at_state<S: StateBackend<H, C>>(
&self,
_state: S,
_changes: &mut OverlayedChanges,
_method: &str,
_call_data: &[u8]
) -> ClientResult<(Vec<u8>, Vec<Vec<u8>>)> {
Err(ClientErrorKind::NotAvailableOnLightClient.into())
}
fn native_runtime_version(&self) -> Option<RuntimeVersion> {
None
}
}
/// Check remote execution proof using given backend.
pub fn check_execution_proof<Header, E, H, C>(
executor: &E,
request: &RemoteCallRequest<Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<CallResult>
where
Header: HeaderT,
E: CodeExecutor<H>,
H: Hasher,
H::Out: Ord + Encodable + HeapSizeOf + From<H256>,
C: NodeCodec<H>,
{
let local_state_root = request.header.state_root();
let mut changes = OverlayedChanges::default();
let (local_result, _) = execution_proof_check::<H, C, _>(
H256::from_slice(local_state_root.as_ref()).into(),
remote_proof,
&mut changes,
executor,
&request.method,
&request.call_data)?;
Ok(CallResult { return_data: local_result, changes })
}
#[cfg(test)]
mod tests {
use test_client;
use executor::NativeExecutionDispatch;
use super::*;
use primitives::RlpCodec;
#[test]
fn execution_proof_is_generated_and_checked() {
// prepare remote client
let remote_client = test_client::new();
let remote_block_id = BlockId::Number(0);
let remote_block_storage_root = remote_client.state_at(&remote_block_id)
.unwrap().storage_root(::std::iter::empty()).0;
// 'fetch' execution proof from remote node
let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap().1;
// check remote execution proof locally
let local_executor = test_client::LocalExecutor::new();
check_execution_proof::<_, _, _, RlpCodec>(&local_executor, &RemoteCallRequest {
block: test_client::runtime::Hash::default(),
header: test_client::runtime::Header {
state_root: remote_block_storage_root.into(),
parent_hash: Default::default(),
number: 0,
extrinsics_root: Default::default(),
digest: Default::default(),
},
method: "authorities".into(),
call_data: vec![],
retry_count: None,
}, remote_execution_proof).unwrap();
}
}
+308
View File
@@ -0,0 +1,308 @@
// Copyright 2017 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 futures::IntoFuture;
use primitives::H256;
use hashdb::Hasher;
use patricia_trie::NodeCodec;
use rlp::Encodable;
use heapsize::HeapSizeOf;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT};
use state_machine::{CodeExecutor, read_proof_check};
use std::marker::PhantomData;
use call_executor::CallResult;
use cht;
use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
use light::call_executor::check_execution_proof;
/// Remote call request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteCallRequest<Header: HeaderT> {
/// Call at state of given block.
pub block: Header::Hash,
/// Head of block at which call is perormed.
pub header: Header,
/// Method to call.
pub method: String,
/// Call data.
pub call_data: Vec<u8>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
/// Remote canonical header request.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct RemoteHeaderRequest<Header: HeaderT> {
/// The root of CHT this block is included in.
pub cht_root: Header::Hash,
/// Number of the header to query.
pub block: Header::Number,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
/// Remote storage read request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteReadRequest<Header: HeaderT> {
/// Read at state of given block.
pub block: Header::Hash,
/// Head of block at which read is perormed.
pub header: Header,
/// Storage key to read.
pub key: Vec<u8>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
/// Light client data fetcher. Implementations of this trait must check if remote data
/// is correct (see FetchedDataChecker) and return already checked data.
pub trait Fetcher<Block: BlockT>: Send + Sync {
/// Remote header future.
type RemoteHeaderResult: IntoFuture<Item=Block::Header, Error=ClientError>;
/// Remote storage read future.
type RemoteReadResult: IntoFuture<Item=Option<Vec<u8>>, Error=ClientError>;
/// Remote call result future.
type RemoteCallResult: IntoFuture<Item=CallResult, Error=ClientError>;
/// Fetch remote header.
fn remote_header(&self, request: RemoteHeaderRequest<Block::Header>) -> Self::RemoteHeaderResult;
/// Fetch remote storage value.
fn remote_read(&self, request: RemoteReadRequest<Block::Header>) -> Self::RemoteReadResult;
/// Fetch remote call result.
fn remote_call(&self, request: RemoteCallRequest<Block::Header>) -> Self::RemoteCallResult;
}
/// Light client remote data checker.
///
/// Implementations of this trait should not use any blockchain data except that is
/// passed to its methods.
pub trait FetchChecker<Block: BlockT>: Send + Sync {
/// Check remote header proof.
fn check_header_proof(
&self,
request: &RemoteHeaderRequest<Block::Header>,
header: Option<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Block::Header>;
/// Check remote storage read proof.
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>>;
/// Check remote method execution proof.
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<CallResult>;
}
/// Remote data checker.
pub struct LightDataChecker<E, H, C> {
executor: E,
_hasher: PhantomData<H>,
_codec: PhantomData<C>,
}
impl<E, H, C> LightDataChecker<E, H, C> {
/// Create new light data checker.
pub fn new(executor: E) -> Self {
Self {
executor, _hasher: PhantomData, _codec: PhantomData
}
}
}
impl<E, Block, H, C> FetchChecker<Block> for LightDataChecker<E, H, C>
where
Block: BlockT,
Block::Hash: Into<H::Out> + From<H256>,
E: CodeExecutor<H>,
H: Hasher,
C: NodeCodec<H> + Sync + Send,
H::Out: Ord + Encodable + HeapSizeOf + From<Block::Hash> + From<H256>,
{
fn check_header_proof(
&self,
request: &RemoteHeaderRequest<Block::Header>,
remote_header: Option<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Block::Header> {
let remote_header = remote_header.ok_or_else(||
ClientError::from(ClientErrorKind::InvalidHeaderProof))?;
let remote_header_hash = remote_header.hash();
cht::check_proof::<Block::Header, H, C>(
request.cht_root,
request.block,
remote_header_hash,
remote_proof)
.map(|_| remote_header)
}
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
let local_state_root = request.header.state_root().clone();
read_proof_check::<H, C>(local_state_root.into(), remote_proof, &request.key).map_err(Into::into)
}
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<CallResult> {
check_execution_proof::<_, _, H, C>(&self.executor, request, remote_proof)
}
}
#[cfg(test)]
pub mod tests {
use futures::future::{ok, err, FutureResult};
use parking_lot::Mutex;
use call_executor::CallResult;
use executor::{self, NativeExecutionDispatch};
use error::Error as ClientError;
use test_client::{self, TestClient, runtime::{Hash, Block, Header}};
use test_client::client::BlockOrigin;
use in_mem::{Blockchain as InMemoryBlockchain};
use light::fetcher::{Fetcher, FetchChecker, LightDataChecker,
RemoteCallRequest, RemoteHeaderRequest};
use primitives::{Blake2Hasher, RlpCodec};
use runtime_primitives::generic::BlockId;
use state_machine::Backend;
use super::*;
pub type OkCallFetcher = Mutex<CallResult>;
impl Fetcher<Block> for OkCallFetcher {
type RemoteHeaderResult = FutureResult<Header, ClientError>;
type RemoteReadResult = FutureResult<Option<Vec<u8>>, ClientError>;
type RemoteCallResult = FutureResult<CallResult, ClientError>;
fn remote_header(&self, _request: RemoteHeaderRequest<Header>) -> Self::RemoteHeaderResult {
err("Not implemented on test node".into())
}
fn remote_read(&self, _request: RemoteReadRequest<Header>) -> Self::RemoteReadResult {
err("Not implemented on test node".into())
}
fn remote_call(&self, _request: RemoteCallRequest<Header>) -> Self::RemoteCallResult {
ok((*self.lock()).clone())
}
}
fn prepare_for_read_proof_check() -> (
LightDataChecker<executor::NativeExecutor<test_client::LocalExecutor>, Blake2Hasher, RlpCodec>,
Header, Vec<Vec<u8>>, usize)
{
// prepare remote client
let remote_client = test_client::new();
let remote_block_id = BlockId::Number(0);
let remote_block_hash = remote_client.block_hash(0).unwrap().unwrap();
let mut remote_block_header = remote_client.header(&remote_block_id).unwrap().unwrap();
remote_block_header.state_root = remote_client.state_at(&remote_block_id).unwrap().storage_root(::std::iter::empty()).0.into();
// 'fetch' read proof from remote node
let authorities_len = remote_client.authorities_at(&remote_block_id).unwrap().len();
let remote_read_proof = remote_client.read_proof(&remote_block_id, b":auth:len").unwrap();
// check remote read proof locally
let local_storage = InMemoryBlockchain::<Block>::new();
local_storage.insert(remote_block_hash, remote_block_header.clone(), None, None, true);
let local_executor = test_client::LocalExecutor::new();
let local_checker = LightDataChecker::new(local_executor);
(local_checker, remote_block_header, remote_read_proof, authorities_len)
}
fn prepare_for_header_proof_check(insert_cht: bool) -> (
LightDataChecker<executor::NativeExecutor<test_client::LocalExecutor>, Blake2Hasher, RlpCodec>,
Hash, Header, Vec<Vec<u8>>)
{
// prepare remote client
let remote_client = test_client::new();
let mut local_headers_hashes = Vec::new();
for i in 0..4 {
let builder = remote_client.new_block().unwrap();
remote_client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
local_headers_hashes.push(remote_client.block_hash(i + 1).unwrap());
}
// 'fetch' header proof from remote node
let remote_block_id = BlockId::Number(1);
let (remote_block_header, remote_header_proof) = remote_client.header_proof_with_cht_size(&remote_block_id, 4).unwrap();
// check remote read proof locally
let local_storage = InMemoryBlockchain::<Block>::new();
let local_cht_root = cht::compute_root::<Header, Blake2Hasher, _>(4, 0, local_headers_hashes.into_iter()).unwrap();
if insert_cht {
local_storage.insert_cht_root(1, local_cht_root);
}
let local_executor = test_client::LocalExecutor::new();
let local_checker = LightDataChecker::new(local_executor);
(local_checker, local_cht_root, remote_block_header, remote_header_proof)
}
#[test]
fn storage_read_proof_is_generated_and_checked() {
let (local_checker, remote_block_header, remote_read_proof, authorities_len) = prepare_for_read_proof_check();
assert_eq!((&local_checker as &FetchChecker<Block>).check_read_proof(&RemoteReadRequest::<Header> {
block: remote_block_header.hash(),
header: remote_block_header,
key: b":auth:len".to_vec(),
retry_count: None,
}, remote_read_proof).unwrap().unwrap()[0], authorities_len as u8);
}
#[test]
fn header_proof_is_generated_and_checked() {
let (local_checker, local_cht_root, remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true);
assert_eq!((&local_checker as &FetchChecker<Block>).check_header_proof(&RemoteHeaderRequest::<Header> {
cht_root: local_cht_root,
block: 1,
retry_count: None,
}, Some(remote_block_header.clone()), remote_header_proof).unwrap(), remote_block_header);
}
#[test]
fn check_header_proof_fails_if_cht_root_is_invalid() {
let (local_checker, _, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true);
remote_block_header.number = 100;
assert!((&local_checker as &FetchChecker<Block>).check_header_proof(&RemoteHeaderRequest::<Header> {
cht_root: Default::default(),
block: 1,
retry_count: None,
}, Some(remote_block_header.clone()), remote_header_proof).is_err());
}
#[test]
fn check_header_proof_fails_if_invalid_header_provided() {
let (local_checker, local_cht_root, mut remote_block_header, remote_header_proof) = prepare_for_header_proof_check(true);
remote_block_header.number = 100;
assert!((&local_checker as &FetchChecker<Block>).check_header_proof(&RemoteHeaderRequest::<Header> {
cht_root: local_cht_root,
block: 1,
retry_count: None,
}, Some(remote_block_header.clone()), remote_header_proof).is_err());
}
}
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2017 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 primitives::{Blake2Hasher, RlpCodec};
use runtime_primitives::BuildStorage;
use runtime_primitives::traits::Block as BlockT;
use state_machine::{CodeExecutor, ExecutionStrategy};
use client::Client;
use error::Result as ClientResult;
use light::backend::Backend;
use light::blockchain::{Blockchain, Storage as BlockchainStorage};
use light::call_executor::RemoteCallExecutor;
use light::fetcher::{Fetcher, LightDataChecker};
use hashdb::Hasher;
use patricia_trie::NodeCodec;
/// Create an instance of light client blockchain backend.
pub fn new_light_blockchain<B: BlockT, S: BlockchainStorage<B>, F>(storage: S) -> Arc<Blockchain<S, F>> {
Arc::new(Blockchain::new(storage))
}
/// Create an instance of light client backend.
pub fn new_light_backend<B: BlockT, S: BlockchainStorage<B>, F: Fetcher<B>>(blockchain: Arc<Blockchain<S, F>>, fetcher: Arc<F>) -> Arc<Backend<S, F>> {
blockchain.set_fetcher(Arc::downgrade(&fetcher));
Arc::new(Backend::new(blockchain))
}
/// Create an instance of light client.
pub fn new_light<B, S, F, GS>(
backend: Arc<Backend<S, F>>,
fetcher: Arc<F>,
genesis_storage: GS,
) -> ClientResult<Client<Backend<S, F>, RemoteCallExecutor<Blockchain<S, F>, F, Blake2Hasher, RlpCodec>, B>>
where
B: BlockT,
S: BlockchainStorage<B>,
F: Fetcher<B>,
GS: BuildStorage,
{
let executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher);
Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible)
}
/// Create an instance of fetch data checker.
pub fn new_fetch_checker<E, H, C>(
executor: E,
) -> LightDataChecker<E, H, C>
where
E: CodeExecutor<H>,
H: Hasher,
C: NodeCodec<H>,
{
LightDataChecker::new(executor)
}