// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! State API backend for full nodes.
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use std::ops::Range;
use futures::{future, StreamExt as _, TryStreamExt as _};
use log::warn;
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
use rpc::{Result as RpcResult, futures::{stream, Future, Sink, Stream, future::result}};
use sc_rpc_api::Subscriptions;
use sc_client_api::backend::Backend;
use sp_blockchain::{
Result as ClientResult, Error as ClientError, HeaderMetadata, CachedHeaderMetadata
};
use sc_client::{
Client, CallExecutor, BlockchainEvents
};
use sp_core::{
Bytes, storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet, ChildInfo},
};
use sp_version::RuntimeVersion;
use sp_runtime::{
generic::BlockId, traits::{Block as BlockT, NumberFor, SaturatedConversion},
};
use sp_api::{Metadata, ProvideRuntimeApi};
use super::{StateBackend, error::{FutureResult, Error, Result}, client_err, child_resolution_error};
/// Ranges to query in state_queryStorage.
struct QueryStorageRange {
/// Hashes of all the blocks in the range.
pub hashes: Vec,
/// Number of the first block in the range.
pub first_number: NumberFor,
/// Blocks subrange ([begin; end) indices within `hashes`) where we should read keys at
/// each state to get changes.
pub unfiltered_range: Range,
/// Blocks subrange ([begin; end) indices within `hashes`) where we could pre-filter
/// blocks-with-changes by using changes tries.
pub filtered_range: Option>,
}
/// State API backend for full nodes.
pub struct FullState {
client: Arc>,
subscriptions: Subscriptions,
}
impl FullState
where
Block: BlockT + 'static,
B: Backend + Send + Sync + 'static,
E: CallExecutor + Send + Sync + 'static + Clone,
{
/// Create new state API backend for full nodes.
pub fn new(client: Arc>, subscriptions: Subscriptions) -> Self {
Self { client, subscriptions }
}
/// Returns given block hash or best block hash if None is passed.
fn block_or_best(&self, hash: Option) -> ClientResult {
Ok(hash.unwrap_or_else(|| self.client.chain_info().best_hash))
}
/// Splits the `query_storage` block range into 'filtered' and 'unfiltered' subranges.
/// Blocks that contain changes within filtered subrange could be filtered using changes tries.
/// Blocks that contain changes within unfiltered subrange must be filtered manually.
fn split_query_storage_range(
&self,
from: Block::Hash,
to: Option
) -> Result> {
let to = self.block_or_best(to).map_err(|e| invalid_block::(from, to, e.to_string()))?;
let invalid_block_err = |e: ClientError| invalid_block::(from, Some(to), e.to_string());
let from_meta = self.client.header_metadata(from).map_err(invalid_block_err)?;
let to_meta = self.client.header_metadata(to).map_err(invalid_block_err)?;
if from_meta.number >= to_meta.number {
return Err(invalid_block_range(&from_meta, &to_meta, "from number >= to number".to_owned()))
}
// check if we can get from `to` to `from` by going through parent_hashes.
let from_number = from_meta.number;
let hashes = {
let mut hashes = vec![to_meta.hash];
let mut last = to_meta.clone();
while last.number > from_number {
let header_metadata = self.client
.header_metadata(last.parent)
.map_err(|e| invalid_block_range::(&last, &to_meta, e.to_string()))?;
hashes.push(header_metadata.hash);
last = header_metadata;
}
if last.hash != from_meta.hash {
return Err(invalid_block_range(&from_meta, &to_meta, "from and to are on different forks".to_owned()))
}
hashes.reverse();
hashes
};
// check if we can filter blocks-with-changes from some (sub)range using changes tries
let changes_trie_range = self.client
.max_key_changes_range(from_number, BlockId::Hash(to_meta.hash))
.map_err(client_err)?;
let filtered_range_begin = changes_trie_range
.map(|(begin, _)| (begin - from_number).saturated_into::());
let (unfiltered_range, filtered_range) = split_range(hashes.len(), filtered_range_begin);
Ok(QueryStorageRange {
hashes,
first_number: from_number,
unfiltered_range,
filtered_range,
})
}
/// Iterates through range.unfiltered_range and check each block for changes of keys' values.
fn query_storage_unfiltered(
&self,
range: &QueryStorageRange,
keys: &[StorageKey],
last_values: &mut HashMap>,
changes: &mut Vec>,
) -> Result<()> {
for block in range.unfiltered_range.start..range.unfiltered_range.end {
let block_hash = range.hashes[block].clone();
let mut block_changes = StorageChangeSet { block: block_hash.clone(), changes: Vec::new() };
let id = BlockId::hash(block_hash);
for key in keys {
let (has_changed, data) = {
let curr_data = self.client.storage(&id, key).map_err(client_err)?;
match last_values.get(key) {
Some(prev_data) => (curr_data != *prev_data, curr_data),
None => (true, curr_data),
}
};
if has_changed {
block_changes.changes.push((key.clone(), data.clone()));
}
last_values.insert(key.clone(), data);
}
if !block_changes.changes.is_empty() {
changes.push(block_changes);
}
}
Ok(())
}
/// Iterates through all blocks that are changing keys within range.filtered_range and collects these changes.
fn query_storage_filtered(
&self,
range: &QueryStorageRange,
keys: &[StorageKey],
last_values: &HashMap>,
changes: &mut Vec>,
) -> Result<()> {
let (begin, end) = match range.filtered_range {
Some(ref filtered_range) => (
range.first_number + filtered_range.start.saturated_into(),
BlockId::Hash(range.hashes[filtered_range.end - 1].clone())
),
None => return Ok(()),
};
let mut changes_map: BTreeMap, StorageChangeSet> = BTreeMap::new();
for key in keys {
let mut last_block = None;
let mut last_value = last_values.get(key).cloned().unwrap_or_default();
let key_changes = self.client.key_changes(begin, end, None, key).map_err(client_err)?;
for (block, _) in key_changes.into_iter().rev() {
if last_block == Some(block) {
continue;
}
let block_hash = range.hashes[(block - range.first_number).saturated_into::()].clone();
let id = BlockId::Hash(block_hash);
let value_at_block = self.client.storage(&id, key).map_err(client_err)?;
if last_value == value_at_block {
continue;
}
changes_map.entry(block)
.or_insert_with(|| StorageChangeSet { block: block_hash, changes: Vec::new() })
.changes.push((key.clone(), value_at_block.clone()));
last_block = Some(block);
last_value = value_at_block;
}
}
if let Some(additional_capacity) = changes_map.len().checked_sub(changes.len()) {
changes.reserve(additional_capacity);
}
changes.extend(changes_map.into_iter().map(|(_, cs)| cs));
Ok(())
}
}
impl StateBackend for FullState where
Block: BlockT + 'static,
B: Backend + Send + Sync + 'static,
E: CallExecutor + Send + Sync + 'static + Clone,
RA: Send + Sync + 'static,
Client: ProvideRuntimeApi,
as ProvideRuntimeApi>::Api:
Metadata,
{
fn call(
&self,
block: Option,
method: String,
call_data: Bytes,
) -> FutureResult {
let r = self.block_or_best(block)
.and_then(|block| self
.client
.executor()
.call(
&BlockId::Hash(block),
&method,
&*call_data,
self.client.execution_extensions().strategies().other,
None,
)
.map(Into::into)
).map_err(client_err);
Box::new(result(r))
}
fn storage_keys(
&self,
block: Option,
prefix: StorageKey,
) -> FutureResult> {
Box::new(result(
self.block_or_best(block)
.and_then(|block| self.client.storage_keys(&BlockId::Hash(block), &prefix))
.map_err(client_err)))
}
fn storage_pairs(
&self,
block: Option,
prefix: StorageKey,
) -> FutureResult> {
Box::new(result(
self.block_or_best(block)
.and_then(|block| self.client.storage_pairs(&BlockId::Hash(block), &prefix))
.map_err(client_err)))
}
fn storage_keys_paged(
&self,
block: Option,
prefix: Option,
count: u32,
start_key: Option,
) -> FutureResult> {
Box::new(result(
self.block_or_best(block)
.and_then(|block|
self.client.storage_keys_iter(
&BlockId::Hash(block), prefix.as_ref(), start_key.as_ref()
)
)
.map(|v| v.take(count as usize).collect())
.map_err(client_err)))
}
fn storage(
&self,
block: Option,
key: StorageKey,
) -> FutureResult