use std::sync::Arc;
use std::collections::{BTreeMap, HashMap};
use std::future::Future;
use sp_runtime::{
traits::{
Block as BlockT, Header as HeaderT, NumberFor,
},
generic::BlockId
};
use sp_core::{ChangesTrieConfigurationRange, storage::PrefixedStorageKey};
use sp_state_machine::StorageProof;
use sp_blockchain::{
HeaderMetadata, well_known_cache_keys, HeaderBackend, Cache as BlockchainCache,
Error as ClientError, Result as ClientResult,
};
use crate::{backend::{AuxStore, NewBlockState}, UsageInfo, ProvideChtRoots};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteCallRequest<Header: HeaderT> {
pub block: Header::Hash,
pub header: Header,
pub method: String,
pub call_data: Vec<u8>,
pub retry_count: Option<usize>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct RemoteHeaderRequest<Header: HeaderT> {
pub cht_root: Header::Hash,
pub block: Header::Number,
pub retry_count: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteReadRequest<Header: HeaderT> {
pub block: Header::Hash,
pub header: Header,
pub keys: Vec<Vec<u8>>,
pub retry_count: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RemoteReadChildRequest<Header: HeaderT> {
pub block: Header::Hash,
pub header: Header,
pub storage_key: PrefixedStorageKey,
pub keys: Vec<Vec<u8>>,
pub retry_count: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RemoteChangesRequest<Header: HeaderT> {
pub changes_trie_configs: Vec<ChangesTrieConfigurationRange<Header::Number, Header::Hash>>,
pub first_block: (Header::Number, Header::Hash),
pub last_block: (Header::Number, Header::Hash),
pub max_block: (Header::Number, Header::Hash),
pub tries_roots: (Header::Number, Header::Hash, Vec<Header::Hash>),
pub storage_key: Option<PrefixedStorageKey>,
pub key: Vec<u8>,
pub retry_count: Option<usize>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct ChangesProof<Header: HeaderT> {
pub max_block: Header::Number,
pub proof: Vec<Vec<u8>>,
pub roots: BTreeMap<Header::Number, Header::Hash>,
pub roots_proof: StorageProof,
}
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
pub struct RemoteBodyRequest<Header: HeaderT> {
pub header: Header,
pub retry_count: Option<usize>,
}
pub trait Fetcher<Block: BlockT>: Send + Sync {
type RemoteHeaderResult: Future<Output = Result<
Block::Header,
ClientError,
>> + Unpin + Send + 'static;
type RemoteReadResult: Future<Output = Result<
HashMap<Vec<u8>, Option<Vec<u8>>>,
ClientError,
>> + Unpin + Send + 'static;
type RemoteCallResult: Future<Output = Result<
Vec<u8>,
ClientError,
>> + Unpin + Send + 'static;
type RemoteChangesResult: Future<Output = Result<
Vec<(NumberFor<Block>, u32)>,
ClientError,
>> + Unpin + Send + 'static;
type RemoteBodyResult: Future<Output = Result<
Vec<Block::Extrinsic>,
ClientError,
>> + Unpin + Send + 'static;
fn remote_header(&self, request: RemoteHeaderRequest<Block::Header>) -> Self::RemoteHeaderResult;
fn remote_read(
&self,
request: RemoteReadRequest<Block::Header>
) -> Self::RemoteReadResult;
fn remote_read_child(
&self,
request: RemoteReadChildRequest<Block::Header>
) -> Self::RemoteReadResult;
fn remote_call(&self, request: RemoteCallRequest<Block::Header>) -> Self::RemoteCallResult;
fn remote_changes(&self, request: RemoteChangesRequest<Block::Header>) -> Self::RemoteChangesResult;
fn remote_body(&self, request: RemoteBodyRequest<Block::Header>) -> Self::RemoteBodyResult;
}
pub trait FetchChecker<Block: BlockT>: Send + Sync {
fn check_header_proof(
&self,
request: &RemoteHeaderRequest<Block::Header>,
header: Option<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<Block::Header>;
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
fn check_execution_proof(
&self,
request: &RemoteCallRequest<Block::Header>,
remote_proof: StorageProof,
) -> ClientResult<Vec<u8>>;
fn check_changes_proof(
&self,
request: &RemoteChangesRequest<Block::Header>,
proof: ChangesProof<Block::Header>
) -> ClientResult<Vec<(NumberFor<Block>, u32)>>;
fn check_body_proof(
&self,
request: &RemoteBodyRequest<Block::Header>,
body: Vec<Block::Extrinsic>
) -> ClientResult<Vec<Block::Extrinsic>>;
}
pub trait Storage<Block: BlockT>: AuxStore + HeaderBackend<Block>
+ HeaderMetadata<Block, Error=ClientError> + ProvideChtRoots<Block>
{
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<()>;
fn set_head(&self, block: BlockId<Block>) -> ClientResult<()>;
fn finalize_header(&self, block: BlockId<Block>) -> ClientResult<()>;
fn last_finalized(&self) -> ClientResult<Block::Hash>;
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>>;
fn usage_info(&self) -> Option<UsageInfo>;
}
#[derive(Debug)]
pub enum LocalOrRemote<Data, Request> {
Local(Data),
Remote(Request),
Unknown,
}
pub trait RemoteBlockchain<Block: BlockT>: Send + Sync {
fn header(&self, id: BlockId<Block>) -> ClientResult<LocalOrRemote<
Block::Header,
RemoteHeaderRequest<Block::Header>,
>>;
}
pub fn future_header<Block: BlockT, F: Fetcher<Block>>(
blockchain: &dyn RemoteBlockchain<Block>,
fetcher: &F,
id: BlockId<Block>,
) -> impl Future<Output = Result<Option<Block::Header>, ClientError>> {
use futures::future::{ready, Either, FutureExt};
match blockchain.header(id) {
Ok(LocalOrRemote::Remote(request)) => Either::Left(
fetcher
.remote_header(request)
.then(|header| ready(header.map(Some)))
),
Ok(LocalOrRemote::Unknown) => Either::Right(ready(Ok(None))),
Ok(LocalOrRemote::Local(local_header)) => Either::Right(ready(Ok(Some(local_header)))),
Err(err) => Either::Right(ready(Err(err))),
}
}
#[cfg(test)]
pub mod tests {
use futures::future::Ready;
use parking_lot::Mutex;
use sp_blockchain::Error as ClientError;
use sp_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>,
{
futures::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 {
futures::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()
}
}
}