1use crate::{
22 archive::{error::Error as ArchiveError, ArchiveApiServer},
23 common::events::{ArchiveStorageResult, PaginatedStorageQuery},
24 hex_string, MethodResult,
25};
26
27use codec::Encode;
28use jsonrpsee::core::{async_trait, RpcResult};
29use sc_client_api::{
30 Backend, BlockBackend, BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, StorageKey,
31 StorageProvider,
32};
33use sp_api::{CallApiAt, CallContext};
34use sp_blockchain::{
35 Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
36};
37use sp_core::{Bytes, U256};
38use sp_runtime::{
39 traits::{Block as BlockT, Header as HeaderT, NumberFor},
40 SaturatedConversion,
41};
42use std::{collections::HashSet, marker::PhantomData, sync::Arc};
43
44use super::archive_storage::ArchiveStorage;
45
46pub struct ArchiveConfig {
48 pub max_descendant_responses: usize,
51 pub max_queried_items: usize,
53}
54
55const MAX_DESCENDANT_RESPONSES: usize = 5;
60
61const MAX_QUERIED_ITEMS: usize = 8;
66
67impl Default for ArchiveConfig {
68 fn default() -> Self {
69 Self {
70 max_descendant_responses: MAX_DESCENDANT_RESPONSES,
71 max_queried_items: MAX_QUERIED_ITEMS,
72 }
73 }
74}
75
76pub struct Archive<BE: Backend<Block>, Block: BlockT, Client> {
78 client: Arc<Client>,
80 backend: Arc<BE>,
82 genesis_hash: String,
84 storage_max_descendant_responses: usize,
87 storage_max_queried_items: usize,
89 _phantom: PhantomData<Block>,
91}
92
93impl<BE: Backend<Block>, Block: BlockT, Client> Archive<BE, Block, Client> {
94 pub fn new<GenesisHash: AsRef<[u8]>>(
96 client: Arc<Client>,
97 backend: Arc<BE>,
98 genesis_hash: GenesisHash,
99 config: ArchiveConfig,
100 ) -> Self {
101 let genesis_hash = hex_string(&genesis_hash.as_ref());
102 Self {
103 client,
104 backend,
105 genesis_hash,
106 storage_max_descendant_responses: config.max_descendant_responses,
107 storage_max_queried_items: config.max_queried_items,
108 _phantom: PhantomData,
109 }
110 }
111}
112
113fn parse_hex_param(param: String) -> Result<Vec<u8>, ArchiveError> {
117 if param.is_empty() {
119 return Ok(Default::default())
120 }
121
122 array_bytes::hex2bytes(¶m).map_err(|_| ArchiveError::InvalidParam(param))
123}
124
125#[async_trait]
126impl<BE, Block, Client> ArchiveApiServer<Block::Hash> for Archive<BE, Block, Client>
127where
128 Block: BlockT + 'static,
129 Block::Header: Unpin,
130 BE: Backend<Block> + 'static,
131 Client: BlockBackend<Block>
132 + ExecutorProvider<Block>
133 + HeaderBackend<Block>
134 + HeaderMetadata<Block, Error = BlockChainError>
135 + BlockchainEvents<Block>
136 + CallApiAt<Block>
137 + StorageProvider<Block, BE>
138 + 'static,
139{
140 fn archive_unstable_body(&self, hash: Block::Hash) -> RpcResult<Option<Vec<String>>> {
141 let Ok(Some(signed_block)) = self.client.block(hash) else { return Ok(None) };
142
143 let extrinsics = signed_block
144 .block
145 .extrinsics()
146 .iter()
147 .map(|extrinsic| hex_string(&extrinsic.encode()))
148 .collect();
149
150 Ok(Some(extrinsics))
151 }
152
153 fn archive_unstable_genesis_hash(&self) -> RpcResult<String> {
154 Ok(self.genesis_hash.clone())
155 }
156
157 fn archive_unstable_header(&self, hash: Block::Hash) -> RpcResult<Option<String>> {
158 let Ok(Some(header)) = self.client.header(hash) else { return Ok(None) };
159
160 Ok(Some(hex_string(&header.encode())))
161 }
162
163 fn archive_unstable_finalized_height(&self) -> RpcResult<u64> {
164 Ok(self.client.info().finalized_number.saturated_into())
165 }
166
167 fn archive_unstable_hash_by_height(&self, height: u64) -> RpcResult<Vec<String>> {
168 let height: NumberFor<Block> = U256::from(height)
169 .try_into()
170 .map_err(|_| ArchiveError::InvalidParam(format!("Invalid block height: {}", height)))?;
171
172 let finalized_num = self.client.info().finalized_number;
173
174 if finalized_num >= height {
175 let Ok(Some(hash)) = self.client.block_hash(height) else { return Ok(vec![]) };
176 return Ok(vec![hex_string(&hash.as_ref())])
177 }
178
179 let blockchain = self.backend.blockchain();
180 let mut headers: Vec<_> = blockchain
182 .leaves()
183 .map_err(|error| ArchiveError::FetchLeaves(error.to_string()))?
184 .into_iter()
185 .filter_map(|hash| {
186 let Ok(Some(header)) = self.client.header(hash) else { return None };
187
188 if header.number() < &height {
189 return None
190 }
191
192 Some(header)
193 })
194 .collect();
195
196 let mut result = Vec::new();
197 let mut visited = HashSet::new();
198
199 while let Some(header) = headers.pop() {
200 if header.number() == &height {
201 result.push(hex_string(&header.hash().as_ref()));
202 continue
203 }
204
205 let parent_hash = *header.parent_hash();
206
207 if visited.insert(parent_hash) {
210 let Ok(Some(next_header)) = self.client.header(parent_hash) else { continue };
211 headers.push(next_header);
212 }
213 }
214
215 Ok(result)
216 }
217
218 fn archive_unstable_call(
219 &self,
220 hash: Block::Hash,
221 function: String,
222 call_parameters: String,
223 ) -> RpcResult<MethodResult> {
224 let call_parameters = Bytes::from(parse_hex_param(call_parameters)?);
225
226 let result =
227 self.client
228 .executor()
229 .call(hash, &function, &call_parameters, CallContext::Offchain);
230
231 Ok(match result {
232 Ok(result) => MethodResult::ok(hex_string(&result)),
233 Err(error) => MethodResult::err(error.to_string()),
234 })
235 }
236
237 fn archive_unstable_storage(
238 &self,
239 hash: Block::Hash,
240 items: Vec<PaginatedStorageQuery<String>>,
241 child_trie: Option<String>,
242 ) -> RpcResult<ArchiveStorageResult> {
243 let items = items
244 .into_iter()
245 .map(|query| {
246 let key = StorageKey(parse_hex_param(query.key)?);
247 let pagination_start_key = query
248 .pagination_start_key
249 .map(|key| parse_hex_param(key).map(|key| StorageKey(key)))
250 .transpose()?;
251
252 if pagination_start_key.is_some() && !query.query_type.is_descendant_query() {
254 return Err(ArchiveError::InvalidParam(
255 "Pagination start key is only supported for descendants queries"
256 .to_string(),
257 ))
258 }
259
260 Ok(PaginatedStorageQuery {
261 key,
262 query_type: query.query_type,
263 pagination_start_key,
264 })
265 })
266 .collect::<Result<Vec<_>, ArchiveError>>()?;
267
268 let child_trie = child_trie
269 .map(|child_trie| parse_hex_param(child_trie))
270 .transpose()?
271 .map(ChildInfo::new_default_from_vec);
272
273 let storage_client = ArchiveStorage::new(
274 self.client.clone(),
275 self.storage_max_descendant_responses,
276 self.storage_max_queried_items,
277 );
278 Ok(storage_client.handle_query(hash, items, child_trie))
279 }
280}