Reorganising the repository - external renames and moves (#4074)

* Adding first rough ouline of the repository structure

* Remove old CI stuff

* add title

* formatting fixes

* move node-exits job's script to scripts dir

* Move docs into subdir

* move to bin

* move maintainence scripts, configs and helpers into its own dir

* add .local to ignore

* move core->client

* start up 'test' area

* move test client

* move test runtime

* make test move compile

* Add dependencies rule enforcement.

* Fix indexing.

* Update docs to reflect latest changes

* Moving /srml->/paint

* update docs

* move client/sr-* -> primitives/

* clean old readme

* remove old broken code in rhd

* update lock

* Step 1.

* starting to untangle client

* Fix after merge.

* start splitting out client interfaces

* move children and blockchain interfaces

* Move trie and state-machine to primitives.

* Fix WASM builds.

* fixing broken imports

* more interface moves

* move backend and light to interfaces

* move CallExecutor

* move cli off client

* moving around more interfaces

* re-add consensus crates into the mix

* fix subkey path

* relieve client from executor

* starting to pull out client from grandpa

* move is_decendent_of out of client

* grandpa still depends on client directly

* lemme tests pass

* rename srml->paint

* Make it compile.

* rename interfaces->client-api

* Move keyring to primitives.

* fixup libp2p dep

* fix broken use

* allow dependency enforcement to fail

* move fork-tree

* Moving wasm-builder

* make env

* move build-script-utils

* fixup broken crate depdencies and names

* fix imports for authority discovery

* fix typo

* update cargo.lock

* fixing imports

* Fix paths and add missing crates

* re-add missing crates
This commit is contained in:
Benjamin Kampmann
2019-11-14 21:51:17 +01:00
committed by Bastian Köcher
parent becc3b0a4f
commit 60e5011c72
809 changed files with 7801 additions and 6464 deletions
+336
View File
@@ -0,0 +1,336 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate Client data backend
use std::sync::Arc;
use std::collections::HashMap;
use primitives::ChangesTrieConfiguration;
use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay};
use sr_primitives::traits::{Block as BlockT, NumberFor};
use state_machine::backend::Backend as StateBackend;
use state_machine::{ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction};
use crate::{
blockchain::{
Backend as BlockchainBackend, well_known_cache_keys
},
offchain::OffchainStorage,
error,
light::RemoteBlockchain,
};
use consensus::BlockOrigin;
use hash_db::Hasher;
use parking_lot::Mutex;
/// In memory array of storage values.
pub type StorageCollection = Vec<(Vec<u8>, Option<Vec<u8>>)>;
/// In memory arrays of storage values for multiple child tries.
pub type ChildStorageCollection = Vec<(Vec<u8>, StorageCollection)>;
pub struct ImportSummary<Block: BlockT> {
pub hash: Block::Hash,
pub origin: BlockOrigin,
pub header: Block::Header,
pub is_new_best: bool,
pub storage_changes: Option<(StorageCollection, ChildStorageCollection)>,
pub retracted: Vec<Block::Hash>,
}
/// Import operation wrapper
pub struct ClientImportOperation<
Block: BlockT,
H: Hasher<Out=Block::Hash>,
B: Backend<Block, H>,
> {
pub op: B::BlockImportOperation,
pub notify_imported: Option<ImportSummary<Block>>,
pub notify_finalized: Vec<Block::Hash>,
}
/// State of a new block.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewBlockState {
/// Normal block.
Normal,
/// New best block.
Best,
/// Newly finalized block (implicitly best).
Final,
}
impl NewBlockState {
/// Whether this block is the new best block.
pub fn is_best(self) -> bool {
match self {
NewBlockState::Best | NewBlockState::Final => true,
NewBlockState::Normal => false,
}
}
/// Whether this block is considered final.
pub fn is_final(self) -> bool {
match self {
NewBlockState::Final => true,
NewBlockState::Best | NewBlockState::Normal => false,
}
}
}
/// Block insertion operation.
///
/// Keeps hold if the inserted block state and data.
pub trait BlockImportOperation<Block, H> where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{
/// Associated state backend type.
type State: StateBackend<H>;
/// Returns pending state.
///
/// Returns None for backends with locally-unavailable state data.
fn state(&self) -> error::Result<Option<&Self::State>>;
/// Append block data to the transaction.
fn set_block_data(
&mut self,
header: Block::Header,
body: Option<Vec<Block::Extrinsic>>,
justification: Option<Justification>,
state: NewBlockState,
) -> error::Result<()>;
/// Update cached data.
fn update_cache(&mut self, cache: HashMap<well_known_cache_keys::Id, Vec<u8>>);
/// Inject storage data into the database.
fn update_db_storage(&mut self, update: <Self::State as StateBackend<H>>::Transaction) -> error::Result<()>;
/// Inject storage data into the database replacing any existing data.
fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> error::Result<H::Out>;
/// Set storage changes.
fn update_storage(
&mut self,
update: StorageCollection,
child_update: ChildStorageCollection,
) -> error::Result<()>;
/// Inject changes trie data into the database.
fn update_changes_trie(&mut self, update: ChangesTrieTransaction<H, NumberFor<Block>>) -> error::Result<()>;
/// Insert auxiliary keys.
///
/// Values are `None` if should be deleted.
fn insert_aux<I>(&mut self, ops: I) -> error::Result<()>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>;
/// Mark a block as finalized.
fn mark_finalized(&mut self, id: BlockId<Block>, justification: Option<Justification>) -> error::Result<()>;
/// Mark a block as new head. If both block import and set head are specified, set head overrides block import's best block rule.
fn mark_head(&mut self, id: BlockId<Block>) -> error::Result<()>;
}
/// Finalize Facilities
pub trait Finalizer<Block: BlockT, H: Hasher<Out=Block::Hash>, B: Backend<Block, H>> {
/// Mark all blocks up to given as finalized in operation.
///
/// If `justification` is provided it is stored with the given finalized
/// block (any other finalized blocks are left unjustified).
///
/// If the block being finalized is on a different fork from the current
/// best block the finalized block is set as best, this might be slightly
/// inaccurate (i.e. outdated). Usages that require determining an accurate
/// best block should use `SelectChain` instead of the client.
fn apply_finality(
&self,
operation: &mut ClientImportOperation<Block, H, B>,
id: BlockId<Block>,
justification: Option<Justification>,
notify: bool,
) -> error::Result<()>;
/// Finalize a block.
///
/// This will implicitly finalize all blocks up to it and
/// fire finality notifications.
///
/// If the block being finalized is on a different fork from the current
/// best block, the finalized block is set as best. This might be slightly
/// inaccurate (i.e. outdated). Usages that require determining an accurate
/// best block should use `SelectChain` instead of the client.
///
/// Pass a flag to indicate whether finality notifications should be propagated.
/// This is usually tied to some synchronization state, where we don't send notifications
/// while performing major synchronization work.
fn finalize_block(
&self,
id: BlockId<Block>,
justification: Option<Justification>,
notify: bool,
) -> error::Result<()>;
}
/// Provides access to an auxiliary database.
pub trait AuxStore {
/// Insert auxiliary data into key-value store.
///
/// Deletions occur after insertions.
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) -> error::Result<()>;
/// Query auxiliary data from key-value store.
fn get_aux(&self, key: &[u8]) -> error::Result<Option<Vec<u8>>>;
}
/// Client backend.
///
/// Manages the data layer.
///
/// Note on state pruning: while an object from `state_at` is alive, the state
/// should not be pruned. The backend should internally reference-count
/// its state objects.
///
/// The same applies for live `BlockImportOperation`s: while an import operation building on a parent `P`
/// is alive, the state for `P` should not be pruned.
pub trait Backend<Block, H>: AuxStore + Send + Sync where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{
/// Associated block insertion operation type.
type BlockImportOperation: BlockImportOperation<Block, H, State=Self::State>;
/// Associated blockchain backend type.
type Blockchain: BlockchainBackend<Block>;
/// Associated state backend type.
type State: StateBackend<H>;
/// Changes trie storage.
type ChangesTrieStorage: PrunableStateChangesTrieStorage<Block, H>;
/// Offchain workers local storage.
type OffchainStorage: OffchainStorage;
/// Begin a new block insertion transaction with given parent block id.
///
/// When constructing the genesis, this is called with all-zero hash.
fn begin_operation(&self) -> error::Result<Self::BlockImportOperation>;
/// Note an operation to contain state transition.
fn begin_state_operation(&self, operation: &mut Self::BlockImportOperation, block: BlockId<Block>) -> error::Result<()>;
/// Commit block insertion.
fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>;
/// Finalize block with given Id.
///
/// This should only be called if the parent of the given block has been finalized.
fn finalize_block(&self, block: BlockId<Block>, justification: Option<Justification>) -> error::Result<()>;
/// Returns reference to blockchain backend.
fn blockchain(&self) -> &Self::Blockchain;
/// Returns the used state cache, if existent.
fn used_state_cache_size(&self) -> Option<usize>;
/// Returns reference to changes trie storage.
fn changes_trie_storage(&self) -> Option<&Self::ChangesTrieStorage>;
/// Returns a handle to offchain storage.
fn offchain_storage(&self) -> Option<Self::OffchainStorage>;
/// Returns true if state for given block is available.
fn have_state_at(&self, hash: &Block::Hash, _number: NumberFor<Block>) -> bool {
self.state_at(BlockId::Hash(hash.clone())).is_ok()
}
/// Returns state backend with post-state of given block.
fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State>;
/// Destroy state and save any useful data, such as cache.
fn destroy_state(&self, _state: Self::State) -> error::Result<()> {
Ok(())
}
/// Attempts to revert the chain by `n` blocks.
///
/// Returns the number of blocks that were successfully reverted.
fn revert(&self, n: NumberFor<Block>) -> error::Result<NumberFor<Block>>;
/// Insert auxiliary data into key-value store.
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) -> error::Result<()>
{
AuxStore::insert_aux(self, insert, delete)
}
/// Query auxiliary data from key-value store.
fn get_aux(&self, key: &[u8]) -> error::Result<Option<Vec<u8>>> {
AuxStore::get_aux(self, key)
}
/// Gain access to the import lock around this backend.
///
/// _Note_ Backend isn't expected to acquire the lock by itself ever. Rather
/// the using components should acquire and hold the lock whenever they do
/// something that the import of a block would interfere with, e.g. importing
/// a new block or calculating the best head.
fn get_import_lock(&self) -> &Mutex<()>;
}
/// Changes trie storage that supports pruning.
pub trait PrunableStateChangesTrieStorage<Block: BlockT, H: Hasher>:
StateChangesTrieStorage<H, NumberFor<Block>>
{
/// Get number block of oldest, non-pruned changes trie.
fn oldest_changes_trie_block(
&self,
config: &ChangesTrieConfiguration,
best_finalized: NumberFor<Block>,
) -> NumberFor<Block>;
}
/// Mark for all Backend implementations, that are making use of state data, stored locally.
pub trait LocalBackend<Block, H>: Backend<Block, H>
where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{}
/// Mark for all Backend implementations, that are fetching required state data from remote nodes.
pub trait RemoteBackend<Block, H>: Backend<Block, H>
where
Block: BlockT,
H: Hasher<Out=Block::Hash>,
{
/// Returns true if the state for given block is available locally.
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool;
/// Returns reference to blockchain backend.
///
/// Returned backend either resolves blockchain data
/// locally, or prepares request to fetch that data from remote node.
fn remote_blockchain(&self) -> Arc<dyn RemoteBlockchain<Block>>;
}
+275
View File
@@ -0,0 +1,275 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate blockchain trait
use std::sync::Arc;
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use sr_primitives::generic::BlockId;
use sr_primitives::Justification;
use log::warn;
use parking_lot::Mutex;
use header_metadata::HeaderMetadata;
use crate::error::{Error, Result};
/// Blockchain database header backend. Does not perform any validation.
pub trait HeaderBackend<Block: BlockT>: Send + Sync {
/// Get block header. Returns `None` if block is not found.
fn header(&self, id: BlockId<Block>) -> Result<Option<Block::Header>>;
/// Get blockchain info.
fn info(&self) -> Info<Block>;
/// Get block status.
fn status(&self, id: BlockId<Block>) -> Result<BlockStatus>;
/// Get block number by hash. Returns `None` if the header is not in the chain.
fn number(&self, hash: Block::Hash) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>;
/// Get block hash by number. Returns `None` if the header is not in the chain.
fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>>;
/// Convert an arbitrary block ID into a block hash.
fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.hash(n),
}
}
/// Convert an arbitrary block ID into a block hash.
fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
match *id {
BlockId::Hash(_) => Ok(self.header(*id)?.map(|h| h.number().clone())),
BlockId::Number(n) => Ok(Some(n)),
}
}
/// Get block header. Returns `UnknownBlock` error if block is not found.
fn expect_header(&self, id: BlockId<Block>) -> Result<Block::Header> {
self.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("{}", id)))
}
/// Convert an arbitrary block ID into a block number. Returns `UnknownBlock` error if block is not found.
fn expect_block_number_from_id(&self, id: &BlockId<Block>) -> Result<NumberFor<Block>> {
self.block_number_from_id(id)
.and_then(|n| n.ok_or_else(|| Error::UnknownBlock(format!("{}", id))))
}
/// Convert an arbitrary block ID into a block hash. Returns `UnknownBlock` error if block is not found.
fn expect_block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Block::Hash> {
self.block_hash_from_id(id)
.and_then(|n| n.ok_or_else(|| Error::UnknownBlock(format!("{}", id))))
}
}
/// Blockchain database backend. Does not perform any validation.
pub trait Backend<Block: BlockT>: HeaderBackend<Block> + HeaderMetadata<Block, Error=Error> {
/// Get block body. Returns `None` if block is not found.
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
/// Get block justification. Returns `None` if justification does not exist.
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>>;
/// Get last finalized block hash.
fn last_finalized(&self) -> Result<Block::Hash>;
/// Returns data cache reference, if it is enabled on this backend.
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
/// Returns hashes of all blocks that are leaves of the block tree.
/// in other words, that have no children, are chain heads.
/// Results must be ordered best (longest, highest) chain first.
fn leaves(&self) -> Result<Vec<Block::Hash>>;
/// Return hashes of all blocks that are children of the block with `parent_hash`.
fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
/// Get the most recent block hash of the best (longest) chains
/// that contain block with the given `target_hash`.
///
/// The search space is always limited to blocks which are in the finalized
/// chain or descendents of it.
///
/// If `maybe_max_block_number` is `Some(max_block_number)`
/// the search is limited to block `numbers <= max_block_number`.
/// in other words as if there were no blocks greater `max_block_number`.
/// Returns `Ok(None)` if `target_hash` is not found in search space.
/// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444)
fn best_containing(
&self,
target_hash: Block::Hash,
maybe_max_number: Option<NumberFor<Block>>,
import_lock: &Mutex<()>,
) -> Result<Option<Block::Hash>> {
let target_header = {
match self.header(BlockId::Hash(target_hash))? {
Some(x) => x,
// target not in blockchain
None => { return Ok(None); },
}
};
if let Some(max_number) = maybe_max_number {
// target outside search range
if target_header.number() > &max_number {
return Ok(None);
}
}
let leaves = {
// ensure no blocks are imported during this code block.
// an import could trigger a reorg which could change the canonical chain.
// we depend on the canonical chain staying the same during this code block.
let _import_guard = import_lock.lock();
let info = self.info();
// this can be `None` if the best chain is shorter than the target header.
let maybe_canon_hash = self.hash(*target_header.number())?;
if maybe_canon_hash.as_ref() == Some(&target_hash) {
// if a `max_number` is given we try to fetch the block at the
// given depth, if it doesn't exist or `max_number` is not
// provided, we continue to search from all leaves below.
if let Some(max_number) = maybe_max_number {
if let Some(header) = self.hash(max_number)? {
return Ok(Some(header));
}
}
} else if info.finalized_number >= *target_header.number() {
// header is on a dead fork.
return Ok(None);
}
self.leaves()?
};
// for each chain. longest chain first. shortest last
for leaf_hash in leaves {
// start at the leaf
let mut current_hash = leaf_hash;
// if search is not restricted then the leaf is the best
let mut best_hash = leaf_hash;
// go backwards entering the search space
// waiting until we are <= max_number
if let Some(max_number) = maybe_max_number {
loop {
let current_header = self.header(BlockId::Hash(current_hash.clone()))?
.ok_or_else(|| Error::from(format!("failed to get header for hash {}", current_hash)))?;
if current_header.number() <= &max_number {
best_hash = current_header.hash();
break;
}
current_hash = *current_header.parent_hash();
}
}
// go backwards through the chain (via parent links)
loop {
// until we find target
if current_hash == target_hash {
return Ok(Some(best_hash));
}
let current_header = self.header(BlockId::Hash(current_hash.clone()))?
.ok_or_else(|| Error::from(format!("failed to get header for hash {}", current_hash)))?;
// stop search in this chain once we go below the target's block number
if current_header.number() < target_header.number() {
break;
}
current_hash = *current_header.parent_hash();
}
}
// header may be on a dead fork -- the only leaves that are considered are
// those which can still be finalized.
//
// FIXME #1558 only issue this warning when not on a dead fork
warn!(
"Block {:?} exists in chain but not found when following all \
leaves backwards. Number limit = {:?}",
target_hash,
maybe_max_number,
);
Ok(None)
}
}
/// Provides access to the optional cache.
pub trait ProvideCache<Block: BlockT> {
/// Returns data cache reference, if it is enabled on this backend.
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
}
/// Blockchain optional data cache.
pub trait Cache<Block: BlockT>: Send + Sync {
/// Initialize genesis value for the given cache.
///
/// The operation should be performed once before anything else is inserted in the cache.
/// Otherwise cache may end up in inconsistent state.
fn initialize(&self, key: &well_known_cache_keys::Id, value_at_genesis: Vec<u8>) -> Result<()>;
/// Returns cached value by the given key.
///
/// Returned tuple is the range where value has been active and the value itself.
fn get_at(
&self,
key: &well_known_cache_keys::Id,
block: &BlockId<Block>,
) -> Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>;
}
/// Blockchain info
#[derive(Debug)]
pub struct Info<Block: BlockT> {
/// Best block hash.
pub best_hash: Block::Hash,
/// Best block number.
pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
/// Genesis block hash.
pub genesis_hash: Block::Hash,
/// The head of the finalized chain.
pub finalized_hash: Block::Hash,
/// Last finalized block number.
pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
}
/// Block status.
#[derive(Debug, PartialEq, Eq)]
pub enum BlockStatus {
/// Already in the blockchain.
InChain,
/// Not in the queue or the blockchain.
Unknown,
}
/// A list of all well known keys in the blockchain cache.
pub mod well_known_cache_keys {
/// The type representing cache keys.
pub type Id = consensus::import_queue::CacheKeyId;
/// A list of authorities.
pub const AUTHORITIES: Id = *b"auth";
/// Current Epoch data.
pub const EPOCH: Id = *b"epch";
/// Changes trie configuration.
pub const CHANGES_TRIE_CONFIG: Id = *b"chtr";
}
+148
View File
@@ -0,0 +1,148 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::{cmp::Ord, panic::UnwindSafe, result, cell::RefCell};
use codec::{Encode, Decode};
use sr_primitives::{
generic::BlockId, traits::Block as BlockT, traits::NumberFor,
};
use state_machine::{
self, OverlayedChanges, ExecutionManager, ExecutionStrategy,
ChangesTrieTransaction, StorageProof,
};
use executor::{RuntimeVersion, NativeVersion};
use hash_db::Hasher;
use primitives::{offchain::OffchainExt, Blake2Hasher, NativeOrEncoded};
use sr_api::{ProofRecorder, InitializeBlock};
use crate::error;
/// Method call executor.
pub trait CallExecutor<B, H>
where
B: BlockT,
H: Hasher<Out=B::Hash>,
H::Out: Ord,
{
/// Externalities error type.
type Error: state_machine::Error;
/// Execute a call to a contract on top of state in a block of given hash.
///
/// No changes are made.
fn call(
&self,
id: &BlockId<B>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
side_effects_handler: Option<OffchainExt>,
) -> Result<Vec<u8>, error::Error>;
/// Execute a contextual call on top of state in a block of a given hash.
///
/// No changes are made.
/// Before executing the method, passed header is installed as the current header
/// of the execution context.
fn contextual_call<
'a,
IB: Fn() -> error::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<B>,
method: &str,
call_data: &[u8],
changes: &RefCell<OverlayedChanges>,
initialize_block: InitializeBlock<'a, B>,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
proof_recorder: &Option<ProofRecorder<B>>,
enable_keystore: bool,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
/// Extract RuntimeVersion of given block
///
/// No changes are made.
fn runtime_version(&self, id: &BlockId<B>) -> Result<RuntimeVersion, error::Error>;
/// Execute a call to a contract on top of given state.
///
/// No changes are made.
fn call_at_state<
S: state_machine::Backend<H>,
F: FnOnce(
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,
state: &S,
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8],
manager: ExecutionManager<F>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
) -> Result<
(
NativeOrEncoded<R>,
(S::Transaction, H::Out),
Option<ChangesTrieTransaction<Blake2Hasher, NumberFor<B>>>
),
error::Error,
>;
/// Execute a call to a contract on top of given state, gathering execution proof.
///
/// No changes are made.
fn prove_at_state<S: state_machine::Backend<H>>(
&self,
mut state: S,
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, StorageProof), error::Error> {
let trie_state = state.as_trie_backend()
.ok_or_else(||
Box::new(state_machine::ExecutionError::UnableToGenerateProof)
as Box<dyn state_machine::Error>
)?;
self.prove_at_trie_state(trie_state, overlay, method, call_data)
}
/// Execute a call to a contract on top of given trie state, gathering execution proof.
///
/// No changes are made.
fn prove_at_trie_state<S: state_machine::TrieBackendStorage<H>>(
&self,
trie_state: &state_machine::TrieBackend<S, H>,
overlay: &mut OverlayedChanges,
method: &str,
call_data: &[u8]
) -> Result<(Vec<u8>, StorageProof), error::Error>;
/// Get runtime version if supported.
fn native_runtime_version(&self) -> Option<&NativeVersion>;
}
+141
View File
@@ -0,0 +1,141 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashMap;
use futures03::channel::mpsc;
use primitives::storage::StorageKey;
use state_machine::ExecutionStrategy;
use sr_primitives::{
traits::{Block as BlockT, NumberFor},
generic::BlockId
};
use consensus::BlockOrigin;
use crate::blockchain::Info;
use crate::notifications::StorageEventStream;
use crate::error;
/// Type that implements `futures::Stream` of block import events.
pub type ImportNotifications<Block> = mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
/// A stream of block finality notifications.
pub type FinalityNotifications<Block> = mpsc::UnboundedReceiver<FinalityNotification<Block>>;
/// Expected hashes of blocks at given heights.
///
/// This may be used as chain spec extension to filter out known, unwanted forks.
pub type ForkBlocks<Block> = Option<HashMap<NumberFor<Block>, <Block as BlockT>::Hash>>;
/// Execution strategies settings.
#[derive(Debug, Clone)]
pub struct ExecutionStrategies {
/// Execution strategy used when syncing.
pub syncing: ExecutionStrategy,
/// Execution strategy used when importing blocks.
pub importing: ExecutionStrategy,
/// Execution strategy used when constructing blocks.
pub block_construction: ExecutionStrategy,
/// Execution strategy used for offchain workers.
pub offchain_worker: ExecutionStrategy,
/// Execution strategy used in other cases.
pub other: ExecutionStrategy,
}
impl Default for ExecutionStrategies {
fn default() -> ExecutionStrategies {
ExecutionStrategies {
syncing: ExecutionStrategy::NativeElseWasm,
importing: ExecutionStrategy::NativeElseWasm,
block_construction: ExecutionStrategy::AlwaysWasm,
offchain_worker: ExecutionStrategy::NativeWhenPossible,
other: ExecutionStrategy::NativeElseWasm,
}
}
}
/// Figure out the block type for a given type (for now, just a `Client`).
pub trait BlockOf {
/// The type of the block.
type Type: BlockT;
}
/// A source of blockchain events.
pub trait BlockchainEvents<Block: BlockT> {
/// Get block import event stream. Not guaranteed to be fired for every
/// imported block.
fn import_notification_stream(&self) -> ImportNotifications<Block>;
/// Get a stream of finality notifications. Not guaranteed to be fired for every
/// finalized block.
fn finality_notification_stream(&self) -> FinalityNotifications<Block>;
/// Get storage changes event stream.
///
/// Passing `None` as `filter_keys` subscribes to all storage changes.
fn storage_changes_notification_stream(
&self,
filter_keys: Option<&[StorageKey]>,
child_filter_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
) -> error::Result<StorageEventStream<Block::Hash>>;
}
/// Fetch block body by ID.
pub trait BlockBody<Block: BlockT> {
/// Get block body by ID. Returns `None` if the body is not stored.
fn block_body(&self,
id: &BlockId<Block>
) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
}
/// Provide a list of potential uncle headers for a given block.
pub trait ProvideUncles<Block: BlockT> {
/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor<Block>)
-> error::Result<Vec<Block::Header>>;
}
/// Client info
#[derive(Debug)]
pub struct ClientInfo<Block: BlockT> {
/// Best block hash.
pub chain: Info<Block>,
/// State Cache Size currently used by the backend
pub used_state_cache_size: Option<usize>,
}
/// Summary of an imported block
#[derive(Clone, Debug)]
pub struct BlockImportNotification<Block: BlockT> {
/// Imported block header hash.
pub hash: Block::Hash,
/// Imported block origin.
pub origin: BlockOrigin,
/// Imported block header.
pub header: Block::Header,
/// Is this the new best block.
pub is_new_best: bool,
/// List of retracted blocks ordered by block number.
pub retracted: Vec<Block::Hash>,
}
/// Summary of a finalized block.
#[derive(Clone, Debug)]
pub struct FinalityNotification<Block: BlockT> {
/// Imported block header hash.
pub hash: Block::Hash,
/// Imported block header.
pub header: Block::Header,
}
+143
View File
@@ -0,0 +1,143 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate client possible errors.
use std::{self, error, result};
use state_machine;
use sr_primitives::ApplyError;
use consensus;
use derive_more::{Display, From};
/// Client Result type alias
pub type Result<T> = result::Result<T, Error>;
/// Substrate Client error
#[derive(Debug, Display, From)]
pub enum Error {
/// Consensus Error
#[display(fmt = "Consensus: {}", _0)]
Consensus(consensus::Error),
/// Backend error.
#[display(fmt = "Backend error: {}", _0)]
Backend(String),
/// Unknown block.
#[display(fmt = "UnknownBlock: {}", _0)]
UnknownBlock(String),
/// Applying extrinsic error.
#[display(fmt = "Extrinsic error: {:?}", _0)]
ApplyExtrinsicFailed(ApplyError),
/// Execution error.
#[display(fmt = "Execution: {}", _0)]
Execution(Box<dyn state_machine::Error>),
/// Blockchain error.
#[display(fmt = "Blockchain: {}", _0)]
Blockchain(Box<Error>),
/// Invalid authorities set received from the runtime.
#[display(fmt = "Current state of blockchain has invalid authorities set")]
InvalidAuthoritiesSet,
/// Could not get runtime version.
#[display(fmt = "On-chain runtime does not specify version")]
VersionInvalid,
/// Genesis config is invalid.
#[display(fmt = "Genesis config provided is invalid")]
GenesisInvalid,
/// Error decoding header justification.
#[display(fmt = "error decoding justification for header")]
JustificationDecode,
/// Justification for header is correctly encoded, but invalid.
#[display(fmt = "bad justification for header: {}", _0)]
BadJustification(String),
/// Not available on light client.
#[display(fmt = "This method is not currently available when running in light client mode")]
NotAvailableOnLightClient,
/// Invalid remote CHT-based proof.
#[display(fmt = "Remote node has responded with invalid header proof")]
InvalidCHTProof,
/// Remote fetch has been cancelled.
#[display(fmt = "Remote data fetch has been cancelled")]
RemoteFetchCancelled,
/// Remote fetch has been failed.
#[display(fmt = "Remote data fetch has been failed")]
RemoteFetchFailed,
/// Error decoding call result.
#[display(fmt = "Error decoding call result of {}: {}", _0, _1)]
CallResultDecode(&'static str, codec::Error),
/// Error converting a parameter between runtime and node.
#[display(fmt = "Error converting `{}` between runtime and node", _0)]
RuntimeParamConversion(String),
/// Changes tries are not supported.
#[display(fmt = "Changes tries are not supported by the runtime")]
ChangesTriesNotSupported,
/// Key changes query has failed.
#[display(fmt = "Failed to check changes proof: {}", _0)]
ChangesTrieAccessFailed(String),
/// Last finalized block not parent of current.
#[display(fmt = "Did not finalize blocks in sequential order.")]
NonSequentialFinalization(String),
/// Safety violation: new best block not descendent of last finalized.
#[display(fmt = "Potential long-range attack: block not in finalized chain.")]
NotInFinalizedChain,
/// Hash that is required for building CHT is missing.
#[display(fmt = "Failed to get hash of block for building CHT")]
MissingHashRequiredForCHT,
/// Invalid calculated state root on block import.
#[display(fmt = "Calculated state root does not match.")]
InvalidStateRoot,
/// A convenience variant for String
#[display(fmt = "{}", _0)]
Msg(String),
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Consensus(e) => Some(e),
Error::Blockchain(e) => Some(e),
_ => None,
}
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Error::Msg(s)
}
}
impl<'a> From<&'a str> for Error {
fn from(s: &'a str) -> Self {
Error::Msg(s.into())
}
}
impl From<block_builder::ApplyExtrinsicFailed> for Error {
fn from(err: block_builder::ApplyExtrinsicFailed) -> Self {
Self::ApplyExtrinsicFailed(err.0)
}
}
impl Error {
/// Chain a blockchain error.
pub fn from_blockchain(e: Box<Error>) -> Self {
Error::Blockchain(e)
}
/// Chain a state error.
pub fn from_state(e: Box<dyn state_machine::Error>) -> Self {
Error::Execution(e)
}
}
+84
View File
@@ -0,0 +1,84 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate client interfaces.
// TODO: make internal
pub mod error;
pub mod backend;
pub mod blockchain;
pub mod light;
pub mod notifications;
pub mod call_executor;
pub mod client;
pub mod offchain;
pub use error::*;
pub use backend::*;
pub use blockchain::*;
pub use light::*;
pub use notifications::*;
pub use call_executor::*;
pub use offchain::*;
pub use client::*;
pub use state_machine::{StorageProof, ExecutionStrategy};
/// Utility methods for the client.
pub mod utils {
use super::HeaderBackend;
use header_metadata::HeaderMetadata;
use crate::error;
use primitives::H256;
use sr_primitives::traits::{Block as BlockT};
use std::borrow::Borrow;
/// Returns a function for checking block ancestry, the returned function will
/// return `true` if the given hash (second parameter) is a descendent of the
/// base (first parameter). If the `current` parameter is defined, it should
/// represent the current block `hash` and its `parent hash`, if given the
/// function that's returned will assume that `hash` isn't part of the local DB
/// yet, and all searches in the DB will instead reference the parent.
pub fn is_descendent_of<'a, Block: BlockT<Hash=H256>, T, H: Borrow<H256> + 'a>(
client: &'a T,
current: Option<(H, H)>,
) -> impl Fn(&H256, &H256) -> Result<bool, error::Error> + 'a
where T: HeaderBackend<Block> + HeaderMetadata<Block, Error=error::Error>,
{
move |base, hash| {
if base == hash { return Ok(false); }
let current = current.as_ref().map(|(c, p)| (c.borrow(), p.borrow()));
let mut hash = hash;
if let Some((current_hash, current_parent_hash)) = current {
if base == current_hash { return Ok(false); }
if hash == current_hash {
if base == current_parent_hash {
return Ok(true);
} else {
hash = current_parent_hash;
}
}
}
let ancestor = header_metadata::lowest_common_ancestor(client, *hash, *base)?;
Ok(ancestor.hash == *base)
}
}
}
+335
View File
@@ -0,0 +1,335 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Substrate light client interfaces
use std::sync::Arc;
use std::collections::{BTreeMap, HashMap};
use std::future::Future;
use sr_primitives::{
traits::{
Block as BlockT, Header as HeaderT, NumberFor,
},
generic::BlockId
};
use primitives::{ChangesTrieConfiguration};
use state_machine::StorageProof;
use header_metadata::HeaderMetadata;
use crate::{
backend::{
AuxStore, NewBlockState,
},
blockchain::{
well_known_cache_keys, HeaderBackend, Cache as BlockchainCache,
},
error::{ Error as ClientError, Result as ClientResult },
};
/// Remote call request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteCallRequest<Header: HeaderT> {
/// Call at state of given block.
pub block: Header::Hash,
/// Header of block at which call is performed.
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,
/// Header of block at which read is performed.
pub header: Header,
/// Storage key to read.
pub keys: Vec<Vec<u8>>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
/// Remote storage read child request.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteReadChildRequest<Header: HeaderT> {
/// Read at state of given block.
pub block: Header::Hash,
/// Header of block at which read is performed.
pub header: Header,
/// Storage key for child.
pub storage_key: Vec<u8>,
/// Child storage key to read.
pub keys: Vec<Vec<u8>>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
/// Remote key changes read request.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RemoteChangesRequest<Header: HeaderT> {
/// Changes trie configuration.
pub changes_trie_config: ChangesTrieConfiguration,
/// Query changes from range of blocks, starting (and including) with this hash...
pub first_block: (Header::Number, Header::Hash),
/// ...ending (and including) with this hash. Should come after first_block and
/// be the part of the same fork.
pub last_block: (Header::Number, Header::Hash),
/// Only use digests from blocks up to this hash. Should be last_block OR come
/// after this block and be the part of the same fork.
pub max_block: (Header::Number, Header::Hash),
/// Known changes trie roots for the range of blocks [tries_roots.0..max_block].
/// Proofs for roots of ascendants of tries_roots.0 are provided by the remote node.
pub tries_roots: (Header::Number, Header::Hash, Vec<Header::Hash>),
/// Optional Child Storage key to read.
pub storage_key: Option<Vec<u8>>,
/// 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>,
}
/// Key changes read proof.
#[derive(Debug, PartialEq, Eq)]
pub struct ChangesProof<Header: HeaderT> {
/// Max block that has been used in changes query.
pub max_block: Header::Number,
/// All touched nodes of all changes tries.
pub proof: Vec<Vec<u8>>,
/// All changes tries roots that have been touched AND are missing from
/// the requester' node. It is a map of block number => changes trie root.
pub roots: BTreeMap<Header::Number, Header::Hash>,
/// The proofs for all changes tries roots that have been touched AND are
/// missing from the requester' node. It is a map of CHT number => proof.
pub roots_proof: StorageProof,
}
/// Remote block body request
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
pub struct RemoteBodyRequest<Header: HeaderT> {
/// Header of the requested block body
pub header: Header,
/// 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: Future<Output = Result<Block::Header, ClientError>> + Send + 'static;
/// Remote storage read future.
type RemoteReadResult: Future<Output = Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>> + Send + 'static;
/// Remote call result future.
type RemoteCallResult: Future<Output = Result<Vec<u8>, ClientError>> + Send + 'static;
/// Remote changes result future.
type RemoteChangesResult: Future<Output = Result<Vec<(NumberFor<Block>, u32)>, ClientError>> + Send + 'static;
/// Remote block body result future.
type RemoteBodyResult: Future<Output = Result<Vec<Block::Extrinsic>, ClientError>> + Send + 'static;
/// 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 storage child value.
fn remote_read_child(
&self,
request: RemoteReadChildRequest<Block::Header>
) -> Self::RemoteReadResult;
/// Fetch remote call result.
fn remote_call(&self, request: RemoteCallRequest<Block::Header>) -> Self::RemoteCallResult;
/// Fetch remote changes ((block number, extrinsic index)) where given key has been changed
/// at a given blocks range.
fn remote_changes(&self, request: RemoteChangesRequest<Block::Header>) -> Self::RemoteChangesResult;
/// Fetch remote block body
fn remote_body(&self, request: RemoteBodyRequest<Block::Header>) -> Self::RemoteBodyResult;
}
/// Light client remote data checker.
///
/// Implementations of this trait should not use any prunable 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: StorageProof,
) -> ClientResult<Block::Header>;
/// Check remote storage read proof.
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote storage read proof.
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote method execution proof.
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>>;
/// Check remote changes query proof.
fn check_changes_proof(
&self,
request: &RemoteChangesRequest<Block::Header>,
proof: ChangesProof<Block::Header>
) -> ClientResult<Vec<(NumberFor<Block>, u32)>>;
/// Check remote body proof.
fn check_body_proof(
&self,
request: &RemoteBodyRequest<Block::Header>,
body: Vec<Block::Extrinsic>
) -> ClientResult<Vec<Block::Extrinsic>>;
}
/// Light client blockchain storage.
pub trait Storage<Block: BlockT>: AuxStore + HeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError> {
/// Store new header. Should refuse to revert any finalized blocks.
///
/// Takes new authorities, the leaf state of the new block, and
/// any auxiliary storage updates to place in the same operation.
fn import_header(
&self,
header: Block::Header,
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> ClientResult<()>;
/// Set an existing block as new best block.
fn set_head(&self, block: BlockId<Block>) -> ClientResult<()>;
/// Mark historic header as finalized.
fn finalize_header(&self, block: BlockId<Block>) -> ClientResult<()>;
/// Get last finalized header.
fn last_finalized(&self) -> ClientResult<Block::Hash>;
/// Get headers CHT root for given block. Fails if the block is not pruned (not a part of any CHT).
fn header_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> ClientResult<Block::Hash>;
/// Get changes trie CHT root for given block. Fails if the block is not pruned (not a part of any CHT).
fn changes_trie_cht_root(
&self,
cht_size: NumberFor<Block>,
block: NumberFor<Block>,
) -> ClientResult<Block::Hash>;
/// Get storage cache.
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>>;
}
/// Remote header.
#[derive(Debug)]
pub enum LocalOrRemote<Data, Request> {
/// When data is available locally, it is returned.
Local(Data),
/// When data is unavailable locally, the request to fetch it from remote node is returned.
Remote(Request),
/// When data is unknown.
Unknown,
}
/// Futures-based blockchain backend that either resolves blockchain data
/// locally, or fetches required data from remote node.
pub trait RemoteBlockchain<Block: BlockT>: Send + Sync {
/// Get block header.
fn header(&self, id: BlockId<Block>) -> ClientResult<LocalOrRemote<
Block::Header,
RemoteHeaderRequest<Block::Header>,
>>;
}
#[cfg(test)]
pub mod tests {
use futures03::future::Ready;
use parking_lot::Mutex;
use crate::error::Error as ClientError;
use test_primitives::{Block, Header, Extrinsic};
use super::*;
pub type OkCallFetcher = Mutex<Vec<u8>>;
fn not_implemented_in_tests<T, E>() -> Ready<Result<T, E>>
where
E: std::convert::From<&'static str>,
{
futures03::future::ready(Err("Not implemented on test node".into()))
}
impl Fetcher<Block> for OkCallFetcher {
type RemoteHeaderResult = Ready<Result<Header, ClientError>>;
type RemoteReadResult = Ready<Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>;
type RemoteCallResult = Ready<Result<Vec<u8>, ClientError>>;
type RemoteChangesResult = Ready<Result<Vec<(NumberFor<Block>, u32)>, ClientError>>;
type RemoteBodyResult = Ready<Result<Vec<Extrinsic>, ClientError>>;
fn remote_header(&self, _request: RemoteHeaderRequest<Header>) -> Self::RemoteHeaderResult {
not_implemented_in_tests()
}
fn remote_read(&self, _request: RemoteReadRequest<Header>) -> Self::RemoteReadResult {
not_implemented_in_tests()
}
fn remote_read_child(&self, _request: RemoteReadChildRequest<Header>) -> Self::RemoteReadResult {
not_implemented_in_tests()
}
fn remote_call(&self, _request: RemoteCallRequest<Header>) -> Self::RemoteCallResult {
futures03::future::ready(Ok((*self.lock()).clone()))
}
fn remote_changes(&self, _request: RemoteChangesRequest<Header>) -> Self::RemoteChangesResult {
not_implemented_in_tests()
}
fn remote_body(&self, _request: RemoteBodyRequest<Header>) -> Self::RemoteBodyResult {
not_implemented_in_tests()
}
}
}
+480
View File
@@ -0,0 +1,480 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Storage notifications
use std::{
collections::{HashSet, HashMap},
sync::Arc,
};
use fnv::{FnvHashSet, FnvHashMap};
use futures03::channel::mpsc;
use primitives::storage::{StorageKey, StorageData};
use sr_primitives::traits::Block as BlockT;
/// Storage change set
#[derive(Debug)]
pub struct StorageChangeSet {
changes: Arc<Vec<(StorageKey, Option<StorageData>)>>,
child_changes: Arc<Vec<(StorageKey, Vec<(StorageKey, Option<StorageData>)>)>>,
filter: Option<HashSet<StorageKey>>,
child_filters: Option<HashMap<StorageKey, Option<HashSet<StorageKey>>>>,
}
impl StorageChangeSet {
/// Convert the change set into iterator over storage items.
pub fn iter<'a>(&'a self)
-> impl Iterator<Item=(Option<&'a StorageKey>, &'a StorageKey, Option<&'a StorageData>)> + 'a {
let top = self.changes
.iter()
.filter(move |&(key, _)| match self.filter {
Some(ref filter) => filter.contains(key),
None => true,
})
.map(move |(k,v)| (None, k, v.as_ref()));
let children = self.child_changes
.iter()
.filter_map(move |(sk, changes)| {
if let Some(cf) = self.child_filters.as_ref() {
if let Some(filter) = cf.get(sk) {
Some(changes
.iter()
.filter(move |&(key, _)| match filter {
Some(ref filter) => filter.contains(key),
None => true,
})
.map(move |(k,v)| (Some(sk), k, v.as_ref())))
} else { None }
} else { None }
})
.flatten();
top.chain(children)
}
}
/// Type that implements `futures::Stream` of storage change events.
pub type StorageEventStream<H> = mpsc::UnboundedReceiver<(H, StorageChangeSet)>;
type SubscriberId = u64;
/// Manages storage listeners.
#[derive(Debug)]
pub struct StorageNotifications<Block: BlockT> {
next_id: SubscriberId,
wildcard_listeners: FnvHashSet<SubscriberId>,
listeners: HashMap<StorageKey, FnvHashSet<SubscriberId>>,
child_listeners: HashMap<StorageKey, (
HashMap<StorageKey, FnvHashSet<SubscriberId>>,
FnvHashSet<SubscriberId>
)>,
sinks: FnvHashMap<SubscriberId, (
mpsc::UnboundedSender<(Block::Hash, StorageChangeSet)>,
Option<HashSet<StorageKey>>,
Option<HashMap<StorageKey, Option<HashSet<StorageKey>>>>,
)>,
}
impl<Block: BlockT> Default for StorageNotifications<Block> {
fn default() -> Self {
StorageNotifications {
next_id: Default::default(),
wildcard_listeners: Default::default(),
listeners: Default::default(),
child_listeners: Default::default(),
sinks: Default::default(),
}
}
}
impl<Block: BlockT> StorageNotifications<Block> {
/// Trigger notification to all listeners.
///
/// Note the changes are going to be filtered by listener's filter key.
/// In fact no event might be sent if clients are not interested in the changes.
pub fn trigger(
&mut self,
hash: &Block::Hash,
changeset: impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
child_changeset: impl Iterator<
Item=(Vec<u8>, impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)>)
>,
) {
let has_wildcard = !self.wildcard_listeners.is_empty();
// early exit if no listeners
if !has_wildcard && self.listeners.is_empty() && self.child_listeners.is_empty() {
return;
}
let mut subscribers = self.wildcard_listeners.clone();
let mut changes = Vec::new();
let mut child_changes = Vec::new();
// Collect subscribers and changes
for (k, v) in changeset {
let k = StorageKey(k);
let listeners = self.listeners.get(&k);
if let Some(ref listeners) = listeners {
subscribers.extend(listeners.iter());
}
if has_wildcard || listeners.is_some() {
changes.push((k, v.map(StorageData)));
}
}
for (sk, changeset) in child_changeset {
let sk = StorageKey(sk);
if let Some((cl, cw)) = self.child_listeners.get(&sk) {
let mut changes = Vec::new();
for (k, v) in changeset {
let k = StorageKey(k);
let listeners = cl.get(&k);
if let Some(ref listeners) = listeners {
subscribers.extend(listeners.iter());
}
subscribers.extend(cw.iter());
if !cw.is_empty() || listeners.is_some() {
changes.push((k, v.map(StorageData)));
}
}
if !changes.is_empty() {
child_changes.push((sk, changes));
}
}
}
// Don't send empty notifications
if changes.is_empty() && child_changes.is_empty() {
return;
}
let changes = Arc::new(changes);
let child_changes = Arc::new(child_changes);
// Trigger the events
for subscriber in subscribers {
let should_remove = {
let &(ref sink, ref filter, ref child_filters) = self.sinks.get(&subscriber)
.expect("subscribers returned from self.listeners are always in self.sinks; qed");
sink.unbounded_send((hash.clone(), StorageChangeSet {
changes: changes.clone(),
child_changes: child_changes.clone(),
filter: filter.clone(),
child_filters: child_filters.clone(),
})).is_err()
};
if should_remove {
self.remove_subscriber(subscriber);
}
}
}
fn remove_subscriber_from(
subscriber: &SubscriberId,
filters: &Option<HashSet<StorageKey>>,
listeners: &mut HashMap<StorageKey, FnvHashSet<SubscriberId>>,
wildcards: &mut FnvHashSet<SubscriberId>,
){
match filters {
None => {
wildcards.remove(subscriber);
},
Some(filters) => {
for key in filters.iter() {
let remove_key = match listeners.get_mut(key) {
Some(ref mut set) => {
set.remove(subscriber);
set.is_empty()
},
None => false,
};
if remove_key {
listeners.remove(key);
}
}
}
}
}
fn remove_subscriber(&mut self, subscriber: SubscriberId) {
if let Some((_, filters, child_filters)) = self.sinks.remove(&subscriber) {
Self::remove_subscriber_from(
&subscriber,
&filters,
&mut self.listeners,
&mut self.wildcard_listeners,
);
if let Some(child_filters) = child_filters.as_ref() {
for (c_key, filters) in child_filters {
if let Some((listeners, wildcards)) = self.child_listeners.get_mut(&c_key) {
Self::remove_subscriber_from(
&subscriber,
&filters,
&mut *listeners,
&mut *wildcards,
);
if listeners.is_empty() && wildcards.is_empty() {
self.child_listeners.remove(&c_key);
}
}
}
}
}
}
fn listen_from(
current_id: SubscriberId,
filter_keys: &Option<impl AsRef<[StorageKey]>>,
listeners: &mut HashMap<StorageKey, FnvHashSet<SubscriberId>>,
wildcards: &mut FnvHashSet<SubscriberId>,
) -> Option<HashSet<StorageKey>>
{
match filter_keys {
None => {
wildcards.insert(current_id);
None
},
Some(keys) => Some(keys.as_ref().iter().map(|key| {
listeners
.entry(key.clone())
.or_insert_with(Default::default)
.insert(current_id);
key.clone()
}).collect())
}
}
/// Start listening for particular storage keys.
pub fn listen(
&mut self,
filter_keys: Option<&[StorageKey]>,
filter_child_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
) -> StorageEventStream<Block::Hash> {
self.next_id += 1;
let current_id = self.next_id;
// add subscriber for every key
let keys = Self::listen_from(
current_id,
&filter_keys,
&mut self.listeners,
&mut self.wildcard_listeners,
);
let child_keys = filter_child_keys.map(|filter_child_keys| {
filter_child_keys.iter().map(|(c_key, o_keys)| {
let (c_listeners, c_wildcards) = self.child_listeners
.entry(c_key.clone())
.or_insert_with(Default::default);
(c_key.clone(), Self::listen_from(
current_id,
o_keys,
&mut *c_listeners,
&mut *c_wildcards,
))
}).collect()
});
// insert sink
let (tx, rx) = mpsc::unbounded();
self.sinks.insert(current_id, (tx, keys, child_keys));
rx
}
}
#[cfg(test)]
mod tests {
use sr_primitives::testing::{H256 as Hash, Block as RawBlock, ExtrinsicWrapper};
use super::*;
use std::iter::{empty, Empty};
type TestChangeSet = (
Vec<(StorageKey, Option<StorageData>)>,
Vec<(StorageKey, Vec<(StorageKey, Option<StorageData>)>)>,
);
#[cfg(test)]
impl From<TestChangeSet> for StorageChangeSet {
fn from(changes: TestChangeSet) -> Self {
// warning hardcoded child trie wildcard to test upon
let child_filters = Some([
(StorageKey(vec![4]), None),
(StorageKey(vec![5]), None),
].into_iter().cloned().collect());
StorageChangeSet {
changes: Arc::new(changes.0),
child_changes: Arc::new(changes.1),
filter: None,
child_filters,
}
}
}
#[cfg(test)]
impl PartialEq for StorageChangeSet {
fn eq(&self, other: &Self) -> bool {
self.iter().eq(other.iter())
}
}
type Block = RawBlock<ExtrinsicWrapper<Hash>>;
#[test]
fn triggering_change_should_notify_wildcard_listeners() {
// given
let mut notifications = StorageNotifications::<Block>::default();
let child_filter = [(StorageKey(vec![4]), None)];
let mut recv = futures03::executor::block_on_stream(
notifications.listen(None, Some(&child_filter[..]))
);
// when
let changeset = vec![
(vec![2], Some(vec![3])),
(vec![3], None),
];
let c_changeset_1 = vec![
(vec![5], Some(vec![4])),
(vec![6], None),
];
let c_changeset = vec![(vec![4], c_changeset_1)];
notifications.trigger(
&Hash::from_low_u64_be(1),
changeset.into_iter(),
c_changeset.into_iter().map(|(a,b)| (a, b.into_iter())),
);
// then
assert_eq!(recv.next().unwrap(), (Hash::from_low_u64_be(1), (vec![
(StorageKey(vec![2]), Some(StorageData(vec![3]))),
(StorageKey(vec![3]), None),
], vec![(StorageKey(vec![4]), vec![
(StorageKey(vec![5]), Some(StorageData(vec![4]))),
(StorageKey(vec![6]), None),
])]).into()));
}
#[test]
fn should_only_notify_interested_listeners() {
// given
let mut notifications = StorageNotifications::<Block>::default();
let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))];
let mut recv1 = futures03::executor::block_on_stream(
notifications.listen(Some(&[StorageKey(vec![1])]), None)
);
let mut recv2 = futures03::executor::block_on_stream(
notifications.listen(Some(&[StorageKey(vec![2])]), None)
);
let mut recv3 = futures03::executor::block_on_stream(
notifications.listen(Some(&[]), Some(&child_filter))
);
// when
let changeset = vec![
(vec![2], Some(vec![3])),
(vec![1], None),
];
let c_changeset_1 = vec![
(vec![5], Some(vec![4])),
(vec![6], None),
];
let c_changeset = vec![(vec![4], c_changeset_1)];
notifications.trigger(
&Hash::from_low_u64_be(1),
changeset.into_iter(),
c_changeset.into_iter().map(|(a,b)| (a, b.into_iter())),
);
// then
assert_eq!(recv1.next().unwrap(), (Hash::from_low_u64_be(1), (vec![
(StorageKey(vec![1]), None),
], vec![]).into()));
assert_eq!(recv2.next().unwrap(), (Hash::from_low_u64_be(1), (vec![
(StorageKey(vec![2]), Some(StorageData(vec![3]))),
], vec![]).into()));
assert_eq!(recv3.next().unwrap(), (Hash::from_low_u64_be(1), (vec![],
vec![
(StorageKey(vec![4]), vec![(StorageKey(vec![5]), Some(StorageData(vec![4])))]),
]).into()));
}
#[test]
fn should_cleanup_subscribers_if_dropped() {
// given
let mut notifications = StorageNotifications::<Block>::default();
{
let child_filter = [(StorageKey(vec![4]), Some(vec![StorageKey(vec![5])]))];
let _recv1 = futures03::executor::block_on_stream(
notifications.listen(Some(&[StorageKey(vec![1])]), None)
);
let _recv2 = futures03::executor::block_on_stream(
notifications.listen(Some(&[StorageKey(vec![2])]), None)
);
let _recv3 = futures03::executor::block_on_stream(
notifications.listen(None, None)
);
let _recv4 = futures03::executor::block_on_stream(
notifications.listen(None, Some(&child_filter))
);
assert_eq!(notifications.listeners.len(), 2);
assert_eq!(notifications.wildcard_listeners.len(), 2);
assert_eq!(notifications.child_listeners.len(), 1);
}
// when
let changeset = vec![
(vec![2], Some(vec![3])),
(vec![1], None),
];
let c_changeset = empty::<(_, Empty<_>)>();
notifications.trigger(&Hash::from_low_u64_be(1), changeset.into_iter(), c_changeset);
// then
assert_eq!(notifications.listeners.len(), 0);
assert_eq!(notifications.wildcard_listeners.len(), 0);
assert_eq!(notifications.child_listeners.len(), 0);
}
#[test]
fn should_not_send_empty_notifications() {
// given
let mut recv = {
let mut notifications = StorageNotifications::<Block>::default();
let recv = futures03::executor::block_on_stream(notifications.listen(None, None));
// when
let changeset = vec![];
let c_changeset = empty::<(_, Empty<_>)>();
notifications.trigger(&Hash::from_low_u64_be(1), changeset.into_iter(), c_changeset);
recv
};
// then
assert_eq!(recv.next(), None);
}
}
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::collections::hash_map::{HashMap, Entry};
/// Offchain workers local storage.
pub trait OffchainStorage: Clone + Send + Sync {
/// Persist a value in storage under given key and prefix.
fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]);
/// Retrieve a value from storage under given key and prefix.
fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>>;
/// Replace the value in storage if given old_value matches the current one.
///
/// Returns `true` if the value has been set and false otherwise.
fn compare_and_set(
&mut self,
prefix: &[u8],
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool;
}
/// In-memory storage for offchain workers.
#[derive(Debug, Clone, Default)]
pub struct InMemOffchainStorage {
storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl OffchainStorage for InMemOffchainStorage {
fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) {
let key = prefix.iter().chain(key).cloned().collect();
self.storage.insert(key, value.to_vec());
}
fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
self.storage.get(&key).cloned()
}
fn compare_and_set(
&mut self,
prefix: &[u8],
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
let key = prefix.iter().chain(key).cloned().collect();
match self.storage.entry(key) {
Entry::Vacant(entry) => if old_value.is_none() {
entry.insert(new_value.to_vec());
true
} else { false },
Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => {
entry.insert(new_value.to_vec());
true
},
_ => false,
}
}
}