use std::sync::Arc;
use futures::{future::ready, FutureExt, TryFutureExt};
use rpc::futures::future::{result, Future, Either};
use tetsy_jsonrpc_pubsub::manager::SubscriptionManager;
use tc_client_api::light::{Fetcher, RemoteBodyRequest, RemoteBlockchain};
use tp_runtime::{
generic::{BlockId, SignedBlock},
traits::{Block as BlockT},
};
use super::{ChainBackend, client_err, error::FutureResult};
use tp_blockchain::HeaderBackend;
use tc_client_api::BlockchainEvents;
pub struct LightChain<Block: BlockT, Client, F> {
client: Arc<Client>,
subscriptions: SubscriptionManager,
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
fetcher: Arc<F>,
}
impl<Block: BlockT, Client, F: Fetcher<Block>> LightChain<Block, Client, F> {
pub fn new(
client: Arc<Client>,
subscriptions: SubscriptionManager,
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
fetcher: Arc<F>,
) -> Self {
Self {
client,
subscriptions,
remote_blockchain,
fetcher,
}
}
}
impl<Block, Client, F> ChainBackend<Client, Block> for LightChain<Block, Client, F> where
Block: BlockT + 'static,
Client: BlockchainEvents<Block> + HeaderBackend<Block> + Send + Sync + 'static,
F: Fetcher<Block> + Send + Sync + 'static,
{
fn client(&self) -> &Arc<Client> {
&self.client
}
fn subscriptions(&self) -> &SubscriptionManager {
&self.subscriptions
}
fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>> {
let hash = self.unwrap_or_best(hash);
let fetcher = self.fetcher.clone();
let maybe_header = tc_client_api::light::future_header(
&*self.remote_blockchain,
&*fetcher,
BlockId::Hash(hash),
);
Box::new(maybe_header.then(move |result|
ready(result.map_err(client_err)),
).boxed().compat())
}
fn block(&self, hash: Option<Block::Hash>)
-> FutureResult<Option<SignedBlock<Block>>>
{
let fetcher = self.fetcher.clone();
let block = self.header(hash)
.and_then(move |header| match header {
Some(header) => Either::A(fetcher
.remote_body(RemoteBodyRequest {
header: header.clone(),
retry_count: Default::default(),
})
.boxed()
.compat()
.map(move |body| Some(SignedBlock {
block: Block::new(header, body),
justification: None,
}))
.map_err(client_err)
),
None => Either::B(result(Ok(None))),
});
Box::new(block)
}
}