1use std::{
4 collections::{BTreeSet, 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;
14use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
15use cuprate_rpc_types::misc::GetOutputsOut;
16use cuprate_types::{
17 blockchain::{BlockchainReadRequest, BlockchainResponse},
18 output_cache::OutputCache,
19 rpc::{
20 ChainInfo, CoinbaseTxSum, KeyImageSpentStatus, OutputDistributionData,
21 OutputHistogramEntry, OutputHistogramInput,
22 },
23 BlockCompleteEntry, Chain, ExtendedBlockHeader, OutputDistributionInput, OutputOnChain,
24 TxInBlockchain,
25};
26
27pub async fn block(
29 blockchain_read: &mut BlockchainReadHandle,
30 height: u64,
31) -> Result<Block, Error> {
32 let BlockchainResponse::Block(block) = blockchain_read
33 .ready()
34 .await?
35 .call(BlockchainReadRequest::Block {
36 height: u64_to_usize(height),
37 })
38 .await?
39 else {
40 unreachable!();
41 };
42
43 Ok(block)
44}
45
46pub async fn block_by_hash(
48 blockchain_read: &mut BlockchainReadHandle,
49 hash: [u8; 32],
50) -> Result<Block, Error> {
51 let BlockchainResponse::Block(block) = blockchain_read
52 .ready()
53 .await?
54 .call(BlockchainReadRequest::BlockByHash(hash))
55 .await?
56 else {
57 unreachable!();
58 };
59
60 Ok(block)
61}
62
63pub async fn block_extended_header(
65 blockchain_read: &mut BlockchainReadHandle,
66 height: u64,
67) -> Result<ExtendedBlockHeader, Error> {
68 let BlockchainResponse::BlockExtendedHeader(header) = blockchain_read
69 .ready()
70 .await?
71 .call(BlockchainReadRequest::BlockExtendedHeader(u64_to_usize(
72 height,
73 )))
74 .await?
75 else {
76 unreachable!();
77 };
78
79 Ok(header)
80}
81
82pub async fn block_hash(
84 blockchain_read: &mut BlockchainReadHandle,
85 height: u64,
86 chain: Chain,
87) -> Result<[u8; 32], Error> {
88 let BlockchainResponse::BlockHash(hash) = blockchain_read
89 .ready()
90 .await?
91 .call(BlockchainReadRequest::BlockHash(
92 u64_to_usize(height),
93 chain,
94 ))
95 .await?
96 else {
97 unreachable!();
98 };
99
100 Ok(hash)
101}
102
103pub async fn find_block(
105 blockchain_read: &mut BlockchainReadHandle,
106 block_hash: [u8; 32],
107) -> Result<Option<(Chain, usize)>, Error> {
108 let BlockchainResponse::FindBlock(option) = blockchain_read
109 .ready()
110 .await?
111 .call(BlockchainReadRequest::FindBlock(block_hash))
112 .await?
113 else {
114 unreachable!();
115 };
116
117 Ok(option)
118}
119
120pub async fn next_chain_entry(
127 blockchain_read: &mut BlockchainReadHandle,
128 block_hashes: Vec<[u8; 32]>,
129 start_height: u64,
130) -> Result<(Vec<[u8; 32]>, Option<usize>, usize), Error> {
131 let BlockchainResponse::NextChainEntry {
132 block_ids,
133 start_height,
134 chain_height,
135 ..
136 } = blockchain_read
137 .ready()
138 .await?
139 .call(BlockchainReadRequest::NextChainEntry(
140 block_hashes,
141 u64_to_usize(start_height),
142 ))
143 .await?
144 else {
145 unreachable!();
146 };
147
148 Ok((block_ids, start_height, chain_height))
149}
150
151pub async fn filter_unknown_hashes(
153 blockchain_read: &mut BlockchainReadHandle,
154 block_hashes: HashSet<[u8; 32]>,
155) -> Result<HashSet<[u8; 32]>, Error> {
156 let BlockchainResponse::FilterUnknownHashes(output) = blockchain_read
157 .ready()
158 .await?
159 .call(BlockchainReadRequest::FilterUnknownHashes(block_hashes))
160 .await?
161 else {
162 unreachable!();
163 };
164
165 Ok(output)
166}
167
168pub async fn block_extended_header_in_range(
170 blockchain_read: &mut BlockchainReadHandle,
171 range: Range<usize>,
172 chain: Chain,
173) -> Result<Vec<ExtendedBlockHeader>, Error> {
174 let BlockchainResponse::BlockExtendedHeaderInRange(output) = blockchain_read
175 .ready()
176 .await?
177 .call(BlockchainReadRequest::BlockExtendedHeaderInRange(
178 range, chain,
179 ))
180 .await?
181 else {
182 unreachable!();
183 };
184
185 Ok(output)
186}
187
188pub async fn chain_height(
190 blockchain_read: &mut BlockchainReadHandle,
191) -> Result<(u64, [u8; 32]), Error> {
192 let BlockchainResponse::ChainHeight(height, hash) = blockchain_read
193 .ready()
194 .await?
195 .call(BlockchainReadRequest::ChainHeight)
196 .await?
197 else {
198 unreachable!();
199 };
200
201 Ok((usize_to_u64(height), hash))
202}
203
204pub async fn generated_coins(
206 blockchain_read: &mut BlockchainReadHandle,
207 block_height: u64,
208) -> Result<u64, Error> {
209 let BlockchainResponse::GeneratedCoins(generated_coins) = blockchain_read
210 .ready()
211 .await?
212 .call(BlockchainReadRequest::GeneratedCoins(u64_to_usize(
213 block_height,
214 )))
215 .await?
216 else {
217 unreachable!();
218 };
219
220 Ok(generated_coins)
221}
222
223pub async fn outputs(
225 blockchain_read: &mut BlockchainReadHandle,
226 outputs: IndexMap<u64, IndexSet<u64>>,
227 get_txid: bool,
228) -> Result<OutputCache, Error> {
229 let BlockchainResponse::Outputs(outputs) = blockchain_read
230 .ready()
231 .await?
232 .call(BlockchainReadRequest::Outputs { outputs, get_txid })
233 .await?
234 else {
235 unreachable!();
236 };
237
238 Ok(outputs)
239}
240
241pub async fn outputs_vec(
243 blockchain_read: &mut BlockchainReadHandle,
244 outputs: Vec<GetOutputsOut>,
245 get_txid: bool,
246) -> Result<Vec<(u64, Vec<(u64, OutputOnChain)>)>, Error> {
247 let outputs = outputs
248 .into_iter()
249 .map(|output| (output.amount, output.index))
250 .collect();
251
252 let BlockchainResponse::OutputsVec(outputs) = blockchain_read
253 .ready()
254 .await?
255 .call(BlockchainReadRequest::OutputsVec { outputs, get_txid })
256 .await?
257 else {
258 unreachable!();
259 };
260
261 Ok(outputs)
262}
263
264pub async fn number_outputs_with_amount(
266 blockchain_read: &mut BlockchainReadHandle,
267 output_amounts: Vec<u64>,
268) -> Result<HashMap<u64, usize>, Error> {
269 let BlockchainResponse::NumberOutputsWithAmount(map) = blockchain_read
270 .ready()
271 .await?
272 .call(BlockchainReadRequest::NumberOutputsWithAmount(
273 output_amounts,
274 ))
275 .await?
276 else {
277 unreachable!();
278 };
279
280 Ok(map)
281}
282
283pub async fn key_images_spent(
285 blockchain_read: &mut BlockchainReadHandle,
286 key_images: HashSet<[u8; 32]>,
287) -> Result<bool, Error> {
288 let BlockchainResponse::KeyImagesSpent(status) = blockchain_read
289 .ready()
290 .await?
291 .call(BlockchainReadRequest::KeyImagesSpent(key_images))
292 .await?
293 else {
294 unreachable!();
295 };
296
297 Ok(status)
298}
299
300pub async fn key_images_spent_vec(
302 blockchain_read: &mut BlockchainReadHandle,
303 key_images: Vec<[u8; 32]>,
304) -> Result<Vec<bool>, Error> {
305 let BlockchainResponse::KeyImagesSpentVec(status) = blockchain_read
306 .ready()
307 .await?
308 .call(BlockchainReadRequest::KeyImagesSpentVec(key_images))
309 .await?
310 else {
311 unreachable!();
312 };
313
314 Ok(status)
315}
316
317pub async fn compact_chain_history(
319 blockchain_read: &mut BlockchainReadHandle,
320) -> Result<(Vec<[u8; 32]>, u128), Error> {
321 let BlockchainResponse::CompactChainHistory {
322 block_ids,
323 cumulative_difficulty,
324 } = blockchain_read
325 .ready()
326 .await?
327 .call(BlockchainReadRequest::CompactChainHistory)
328 .await?
329 else {
330 unreachable!();
331 };
332
333 Ok((block_ids, cumulative_difficulty))
334}
335
336pub async fn find_first_unknown(
338 blockchain_read: &mut BlockchainReadHandle,
339 hashes: Vec<[u8; 32]>,
340) -> Result<Option<(usize, u64)>, Error> {
341 let BlockchainResponse::FindFirstUnknown(resp) = blockchain_read
342 .ready()
343 .await?
344 .call(BlockchainReadRequest::FindFirstUnknown(hashes))
345 .await?
346 else {
347 unreachable!();
348 };
349
350 Ok(resp.map(|(index, height)| (index, usize_to_u64(height))))
351}
352
353pub async fn total_tx_count(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
355 let BlockchainResponse::TotalTxCount(tx_count) = blockchain_read
356 .ready()
357 .await?
358 .call(BlockchainReadRequest::TotalTxCount)
359 .await?
360 else {
361 unreachable!();
362 };
363
364 Ok(usize_to_u64(tx_count))
365}
366
367pub async fn database_size(
369 blockchain_read: &mut BlockchainReadHandle,
370) -> Result<(u64, u64), Error> {
371 let BlockchainResponse::DatabaseSize {
372 database_size,
373 free_space,
374 } = blockchain_read
375 .ready()
376 .await?
377 .call(BlockchainReadRequest::DatabaseSize)
378 .await?
379 else {
380 unreachable!();
381 };
382
383 Ok((database_size, free_space))
384}
385
386pub async fn output_distribution(
388 blockchain_read: &mut BlockchainReadHandle,
389 input: OutputDistributionInput,
390) -> Result<Vec<OutputDistributionData>, Error> {
391 let BlockchainResponse::OutputDistribution(data) = blockchain_read
392 .ready()
393 .await?
394 .call(BlockchainReadRequest::OutputDistribution(input))
395 .await?
396 else {
397 unreachable!();
398 };
399
400 Ok(data)
401}
402
403pub async fn output_histogram(
405 blockchain_read: &mut BlockchainReadHandle,
406 input: OutputHistogramInput,
407) -> Result<Vec<OutputHistogramEntry>, Error> {
408 let BlockchainResponse::OutputHistogram(histogram) = blockchain_read
409 .ready()
410 .await?
411 .call(BlockchainReadRequest::OutputHistogram(input))
412 .await?
413 else {
414 unreachable!();
415 };
416
417 Ok(histogram)
418}
419
420pub async fn coinbase_tx_sum(
422 blockchain_read: &mut BlockchainReadHandle,
423 height: u64,
424 count: u64,
425) -> Result<CoinbaseTxSum, Error> {
426 let BlockchainResponse::CoinbaseTxSum(sum) = blockchain_read
427 .ready()
428 .await?
429 .call(BlockchainReadRequest::CoinbaseTxSum {
430 height: u64_to_usize(height),
431 count,
432 })
433 .await?
434 else {
435 unreachable!();
436 };
437
438 Ok(sum)
439}
440
441pub async fn alt_chains(
443 blockchain_read: &mut BlockchainReadHandle,
444) -> Result<Vec<ChainInfo>, Error> {
445 let BlockchainResponse::AltChains(vec) = blockchain_read
446 .ready()
447 .await?
448 .call(BlockchainReadRequest::AltChains)
449 .await?
450 else {
451 unreachable!();
452 };
453
454 Ok(vec)
455}
456
457pub async fn alt_chain_count(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
459 let BlockchainResponse::AltChainCount(count) = blockchain_read
460 .ready()
461 .await?
462 .call(BlockchainReadRequest::AltChainCount)
463 .await?
464 else {
465 unreachable!();
466 };
467
468 Ok(usize_to_u64(count))
469}
470
471pub async fn transactions(
473 blockchain_read: &mut BlockchainReadHandle,
474 tx_hashes: HashSet<[u8; 32]>,
475) -> Result<(Vec<TxInBlockchain>, Vec<[u8; 32]>), Error> {
476 let BlockchainResponse::Transactions { txs, missed_txs } = blockchain_read
477 .ready()
478 .await?
479 .call(BlockchainReadRequest::Transactions { tx_hashes })
480 .await?
481 else {
482 unreachable!();
483 };
484
485 Ok((txs, missed_txs))
486}
487
488pub async fn total_rct_outputs(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
490 let BlockchainResponse::TotalRctOutputs(n) = blockchain_read
491 .ready()
492 .await?
493 .call(BlockchainReadRequest::TotalRctOutputs)
494 .await?
495 else {
496 unreachable!();
497 };
498
499 Ok(n)
500}
501
502pub async fn block_complete_entries(
504 blockchain_read: &mut BlockchainReadHandle,
505 block_hashes: Vec<[u8; 32]>,
506) -> Result<(Vec<BlockCompleteEntry>, Vec<[u8; 32]>, usize), Error> {
507 let BlockchainResponse::BlockCompleteEntries {
508 blocks,
509 missing_hashes,
510 blockchain_height,
511 } = blockchain_read
512 .ready()
513 .await?
514 .call(BlockchainReadRequest::BlockCompleteEntries(block_hashes))
515 .await?
516 else {
517 unreachable!();
518 };
519
520 Ok((blocks, missing_hashes, blockchain_height))
521}
522
523pub async fn block_complete_entries_by_height(
525 blockchain_read: &mut BlockchainReadHandle,
526 block_heights: Vec<u64>,
527) -> Result<Vec<BlockCompleteEntry>, Error> {
528 let BlockchainResponse::BlockCompleteEntriesByHeight(blocks) = blockchain_read
529 .ready()
530 .await?
531 .call(BlockchainReadRequest::BlockCompleteEntriesByHeight(
532 block_heights.into_iter().map(u64_to_usize).collect(),
533 ))
534 .await?
535 else {
536 unreachable!();
537 };
538
539 Ok(blocks)
540}
541
542pub async fn tx_output_indexes(
544 blockchain_read: &mut BlockchainReadHandle,
545 tx_hash: [u8; 32],
546) -> Result<Vec<u64>, Error> {
547 let BlockchainResponse::TxOutputIndexes(o_indexes) = blockchain_read
548 .ready()
549 .await?
550 .call(BlockchainReadRequest::TxOutputIndexes { tx_hash })
551 .await?
552 else {
553 unreachable!();
554 };
555
556 Ok(o_indexes)
557}