1use super::Block;
6use crate::{
7 client::OnlineClientT,
8 config::{Config, Header},
9 error::{BlockError, Error},
10 utils::PhantomDataSendSync,
11};
12use derivative::Derivative;
13use futures::{future::Either, stream, Stream, StreamExt};
14use std::{future::Future, pin::Pin};
15
16type BlockStream<T> = Pin<Box<dyn Stream<Item = Result<T, Error>> + Send>>;
17type BlockStreamRes<T> = Result<BlockStream<T>, Error>;
18
19#[derive(Derivative)]
21#[derivative(Clone(bound = "Client: Clone"))]
22pub struct BlocksClient<T, Client> {
23 client: Client,
24 _marker: PhantomDataSendSync<T>,
25}
26
27impl<T, Client> BlocksClient<T, Client> {
28 pub fn new(client: Client) -> Self {
30 Self {
31 client,
32 _marker: PhantomDataSendSync::new(),
33 }
34 }
35}
36
37impl<T, Client> BlocksClient<T, Client>
38where
39 T: Config,
40 Client: OnlineClientT<T>,
41{
42 pub fn at(
50 &self,
51 block_hash: T::Hash,
52 ) -> impl Future<Output = Result<Block<T, Client>, Error>> + Send + 'static {
53 self.at_or_latest(Some(block_hash))
54 }
55
56 pub fn at_latest(
58 &self,
59 ) -> impl Future<Output = Result<Block<T, Client>, Error>> + Send + 'static {
60 self.at_or_latest(None)
61 }
62
63 fn at_or_latest(
66 &self,
67 block_hash: Option<T::Hash>,
68 ) -> impl Future<Output = Result<Block<T, Client>, Error>> + Send + 'static {
69 let client = self.client.clone();
70 async move {
71 let block_hash = match block_hash {
74 Some(hash) => hash,
75 None => client
76 .rpc()
77 .block_hash(None)
78 .await?
79 .expect("didn't pass a block number; qed"),
80 };
81
82 let block_header = match client.rpc().header(Some(block_hash)).await? {
83 Some(header) => header,
84 None => return Err(BlockError::not_found(block_hash).into()),
85 };
86
87 Ok(Block::new(block_header, client))
88 }
89 }
90
91 pub fn subscribe_all(
96 &self,
97 ) -> impl Future<Output = Result<BlockStream<Block<T, Client>>, Error>> + Send + 'static
98 where
99 Client: Send + Sync + 'static,
100 {
101 let client = self.client.clone();
102 header_sub_fut_to_block_sub(self.clone(), async move {
103 let sub = client.rpc().subscribe_all_block_headers().await?;
104 BlockStreamRes::Ok(Box::pin(sub))
105 })
106 }
107
108 pub fn subscribe_best(
113 &self,
114 ) -> impl Future<Output = Result<BlockStream<Block<T, Client>>, Error>> + Send + 'static
115 where
116 Client: Send + Sync + 'static,
117 {
118 let client = self.client.clone();
119 header_sub_fut_to_block_sub(self.clone(), async move {
120 let sub = client.rpc().subscribe_best_block_headers().await?;
121 BlockStreamRes::Ok(Box::pin(sub))
122 })
123 }
124
125 pub fn subscribe_finalized(
127 &self,
128 ) -> impl Future<Output = Result<BlockStream<Block<T, Client>>, Error>> + Send + 'static
129 where
130 Client: Send + Sync + 'static,
131 {
132 let client = self.client.clone();
133 header_sub_fut_to_block_sub(self.clone(), async move {
134 let last_finalized_block_hash = client.rpc().finalized_head().await?;
137 let last_finalized_block_num = client
138 .rpc()
139 .header(Some(last_finalized_block_hash))
140 .await?
141 .map(|h| h.number().into());
142
143 let sub = client.rpc().subscribe_finalized_block_headers().await?;
144
145 BlockStreamRes::Ok(
147 subscribe_to_block_headers_filling_in_gaps(client, last_finalized_block_num, sub)
148 .boxed(),
149 )
150 })
151 }
152}
153
154async fn header_sub_fut_to_block_sub<T, Client, S>(
157 blocks_client: BlocksClient<T, Client>,
158 sub: S,
159) -> Result<BlockStream<Block<T, Client>>, Error>
160where
161 T: Config,
162 S: Future<Output = Result<BlockStream<T::Header>, Error>> + Send + 'static,
163 Client: OnlineClientT<T> + Send + Sync + 'static,
164{
165 let sub = sub.await?.then(move |header| {
166 let client = blocks_client.client.clone();
167 async move {
168 let header = match header {
169 Ok(header) => header,
170 Err(e) => return Err(e),
171 };
172
173 Ok(Block::new(header, client))
174 }
175 });
176 BlockStreamRes::Ok(Box::pin(sub))
177}
178
179#[doc(hidden)]
182pub fn subscribe_to_block_headers_filling_in_gaps<T, Client, S, E>(
183 client: Client,
184 mut last_block_num: Option<u64>,
185 sub: S,
186) -> impl Stream<Item = Result<T::Header, Error>> + Send
187where
188 T: Config,
189 Client: OnlineClientT<T>,
190 S: Stream<Item = Result<T::Header, E>> + Send,
191 E: Into<Error> + Send + 'static,
192{
193 sub.flat_map(move |s| {
194 let client = client.clone();
195
196 let header = match s {
198 Ok(header) => header,
199 Err(e) => return Either::Left(stream::once(async { Err(e.into()) })),
200 };
201
202 let end_block_num = header.number().into();
204
205 let start_block_num = last_block_num.map(|n| n + 1).unwrap_or(end_block_num);
207
208 let previous_headers = stream::iter(start_block_num..end_block_num)
211 .then(move |n| {
212 let rpc = client.rpc().clone();
213 async move {
214 let hash = rpc.block_hash(Some(n.into())).await?;
215 let header = rpc.header(hash).await?;
216 Ok::<_, Error>(header)
217 }
218 })
219 .filter_map(|h| async { h.transpose() });
220
221 last_block_num = Some(end_block_num);
223
224 Either::Right(previous_headers.chain(stream::once(async { Ok(header) })))
226 })
227}