1use std::{
4 collections::{BTreeMap, HashMap, HashSet},
5 ops::Range,
6};
7
8use anyhow::Error;
9use indexmap::{IndexMap, IndexSet};
10use monero_serai::block::Block;
11use tower::{Service, ServiceExt};
12
13use cuprate_blockchain::{service::BlockchainReadHandle, types::AltChainInfo};
14use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
15use cuprate_types::{
16 blockchain::{BlockchainReadRequest, BlockchainResponse},
17 output_cache::OutputCache,
18 Chain, ChainInfo, CoinbaseTxSum, ExtendedBlockHeader, HardFork, MinerData,
19 OutputHistogramEntry, OutputHistogramInput, OutputOnChain,
20};
21
22pub(crate) async fn block(
24 blockchain_read: &mut BlockchainReadHandle,
25 height: u64,
26) -> Result<Block, Error> {
27 let BlockchainResponse::Block(block) = blockchain_read
28 .ready()
29 .await?
30 .call(BlockchainReadRequest::Block {
31 height: u64_to_usize(height),
32 })
33 .await?
34 else {
35 unreachable!();
36 };
37
38 Ok(block)
39}
40
41pub(crate) async fn block_by_hash(
43 blockchain_read: &mut BlockchainReadHandle,
44 hash: [u8; 32],
45) -> Result<Block, Error> {
46 let BlockchainResponse::Block(block) = blockchain_read
47 .ready()
48 .await?
49 .call(BlockchainReadRequest::BlockByHash(hash))
50 .await?
51 else {
52 unreachable!();
53 };
54
55 Ok(block)
56}
57
58pub(crate) async fn block_extended_header(
60 blockchain_read: &mut BlockchainReadHandle,
61 height: u64,
62) -> Result<ExtendedBlockHeader, Error> {
63 let BlockchainResponse::BlockExtendedHeader(header) = blockchain_read
64 .ready()
65 .await?
66 .call(BlockchainReadRequest::BlockExtendedHeader(u64_to_usize(
67 height,
68 )))
69 .await?
70 else {
71 unreachable!();
72 };
73
74 Ok(header)
75}
76
77pub(crate) async fn block_hash(
79 blockchain_read: &mut BlockchainReadHandle,
80 height: u64,
81 chain: Chain,
82) -> Result<[u8; 32], Error> {
83 let BlockchainResponse::BlockHash(hash) = blockchain_read
84 .ready()
85 .await?
86 .call(BlockchainReadRequest::BlockHash(
87 u64_to_usize(height),
88 chain,
89 ))
90 .await?
91 else {
92 unreachable!();
93 };
94
95 Ok(hash)
96}
97
98pub(crate) async fn find_block(
100 blockchain_read: &mut BlockchainReadHandle,
101 block_hash: [u8; 32],
102) -> Result<Option<(Chain, usize)>, Error> {
103 let BlockchainResponse::FindBlock(option) = blockchain_read
104 .ready()
105 .await?
106 .call(BlockchainReadRequest::FindBlock(block_hash))
107 .await?
108 else {
109 unreachable!();
110 };
111
112 Ok(option)
113}
114
115pub(crate) async fn filter_unknown_hashes(
117 blockchain_read: &mut BlockchainReadHandle,
118 block_hashes: HashSet<[u8; 32]>,
119) -> Result<HashSet<[u8; 32]>, Error> {
120 let BlockchainResponse::FilterUnknownHashes(output) = blockchain_read
121 .ready()
122 .await?
123 .call(BlockchainReadRequest::FilterUnknownHashes(block_hashes))
124 .await?
125 else {
126 unreachable!();
127 };
128
129 Ok(output)
130}
131
132pub(crate) async fn block_extended_header_in_range(
134 blockchain_read: &mut BlockchainReadHandle,
135 range: Range<usize>,
136 chain: Chain,
137) -> Result<Vec<ExtendedBlockHeader>, Error> {
138 let BlockchainResponse::BlockExtendedHeaderInRange(output) = blockchain_read
139 .ready()
140 .await?
141 .call(BlockchainReadRequest::BlockExtendedHeaderInRange(
142 range, chain,
143 ))
144 .await?
145 else {
146 unreachable!();
147 };
148
149 Ok(output)
150}
151
152pub(crate) async fn chain_height(
154 blockchain_read: &mut BlockchainReadHandle,
155) -> Result<(u64, [u8; 32]), Error> {
156 let BlockchainResponse::ChainHeight(height, hash) = blockchain_read
157 .ready()
158 .await?
159 .call(BlockchainReadRequest::ChainHeight)
160 .await?
161 else {
162 unreachable!();
163 };
164
165 Ok((usize_to_u64(height), hash))
166}
167
168pub(crate) async fn generated_coins(
170 blockchain_read: &mut BlockchainReadHandle,
171 block_height: u64,
172) -> Result<u64, Error> {
173 let BlockchainResponse::GeneratedCoins(generated_coins) = blockchain_read
174 .ready()
175 .await?
176 .call(BlockchainReadRequest::GeneratedCoins(u64_to_usize(
177 block_height,
178 )))
179 .await?
180 else {
181 unreachable!();
182 };
183
184 Ok(generated_coins)
185}
186
187pub(crate) async fn outputs(
189 blockchain_read: &mut BlockchainReadHandle,
190 outputs: IndexMap<u64, IndexSet<u64>>,
191) -> Result<OutputCache, Error> {
192 let BlockchainResponse::Outputs(outputs) = blockchain_read
193 .ready()
194 .await?
195 .call(BlockchainReadRequest::Outputs(outputs))
196 .await?
197 else {
198 unreachable!();
199 };
200
201 Ok(outputs)
202}
203
204pub(crate) async fn number_outputs_with_amount(
206 blockchain_read: &mut BlockchainReadHandle,
207 output_amounts: Vec<u64>,
208) -> Result<HashMap<u64, usize>, Error> {
209 let BlockchainResponse::NumberOutputsWithAmount(map) = blockchain_read
210 .ready()
211 .await?
212 .call(BlockchainReadRequest::NumberOutputsWithAmount(
213 output_amounts,
214 ))
215 .await?
216 else {
217 unreachable!();
218 };
219
220 Ok(map)
221}
222
223pub(crate) async fn key_images_spent(
225 blockchain_read: &mut BlockchainReadHandle,
226 key_images: HashSet<[u8; 32]>,
227) -> Result<bool, Error> {
228 let BlockchainResponse::KeyImagesSpent(is_spent) = blockchain_read
229 .ready()
230 .await?
231 .call(BlockchainReadRequest::KeyImagesSpent(key_images))
232 .await?
233 else {
234 unreachable!();
235 };
236
237 Ok(is_spent)
238}
239
240pub(crate) async fn compact_chain_history(
242 blockchain_read: &mut BlockchainReadHandle,
243) -> Result<(Vec<[u8; 32]>, u128), Error> {
244 let BlockchainResponse::CompactChainHistory {
245 block_ids,
246 cumulative_difficulty,
247 } = blockchain_read
248 .ready()
249 .await?
250 .call(BlockchainReadRequest::CompactChainHistory)
251 .await?
252 else {
253 unreachable!();
254 };
255
256 Ok((block_ids, cumulative_difficulty))
257}
258
259pub(crate) async fn find_first_unknown(
261 blockchain_read: &mut BlockchainReadHandle,
262 hashes: Vec<[u8; 32]>,
263) -> Result<Option<(usize, u64)>, Error> {
264 let BlockchainResponse::FindFirstUnknown(resp) = blockchain_read
265 .ready()
266 .await?
267 .call(BlockchainReadRequest::FindFirstUnknown(hashes))
268 .await?
269 else {
270 unreachable!();
271 };
272
273 Ok(resp.map(|(index, height)| (index, usize_to_u64(height))))
274}
275
276pub(crate) async fn total_tx_count(
278 blockchain_read: &mut BlockchainReadHandle,
279) -> Result<u64, Error> {
280 let BlockchainResponse::TotalTxCount(tx_count) = blockchain_read
281 .ready()
282 .await?
283 .call(BlockchainReadRequest::TotalTxCount)
284 .await?
285 else {
286 unreachable!();
287 };
288
289 Ok(usize_to_u64(tx_count))
290}
291
292pub(crate) async fn database_size(
294 blockchain_read: &mut BlockchainReadHandle,
295) -> Result<(u64, u64), Error> {
296 let BlockchainResponse::DatabaseSize {
297 database_size,
298 free_space,
299 } = blockchain_read
300 .ready()
301 .await?
302 .call(BlockchainReadRequest::DatabaseSize)
303 .await?
304 else {
305 unreachable!();
306 };
307
308 Ok((database_size, free_space))
309}
310
311pub(crate) async fn output_histogram(
313 blockchain_read: &mut BlockchainReadHandle,
314 input: OutputHistogramInput,
315) -> Result<Vec<OutputHistogramEntry>, Error> {
316 let BlockchainResponse::OutputHistogram(histogram) = blockchain_read
317 .ready()
318 .await?
319 .call(BlockchainReadRequest::OutputHistogram(input))
320 .await?
321 else {
322 unreachable!();
323 };
324
325 Ok(histogram)
326}
327
328pub(crate) async fn coinbase_tx_sum(
330 blockchain_read: &mut BlockchainReadHandle,
331 height: u64,
332 count: u64,
333) -> Result<CoinbaseTxSum, Error> {
334 let BlockchainResponse::CoinbaseTxSum(sum) = blockchain_read
335 .ready()
336 .await?
337 .call(BlockchainReadRequest::CoinbaseTxSum {
338 height: u64_to_usize(height),
339 count,
340 })
341 .await?
342 else {
343 unreachable!();
344 };
345
346 Ok(sum)
347}
348
349pub(crate) async fn alt_chains(
351 blockchain_read: &mut BlockchainReadHandle,
352) -> Result<Vec<ChainInfo>, Error> {
353 let BlockchainResponse::AltChains(vec) = blockchain_read
354 .ready()
355 .await?
356 .call(BlockchainReadRequest::AltChains)
357 .await?
358 else {
359 unreachable!();
360 };
361
362 Ok(vec)
363}
364
365pub(crate) async fn alt_chain_count(
367 blockchain_read: &mut BlockchainReadHandle,
368) -> Result<u64, Error> {
369 let BlockchainResponse::AltChainCount(count) = blockchain_read
370 .ready()
371 .await?
372 .call(BlockchainReadRequest::AltChainCount)
373 .await?
374 else {
375 unreachable!();
376 };
377
378 Ok(usize_to_u64(count))
379}