cuprated/rpc/request/
blockchain.rs

1//! Functions for [`BlockchainReadRequest`].
2
3use 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
22/// [`BlockchainReadRequest::Block`].
23pub(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
41/// [`BlockchainReadRequest::BlockByHash`].
42pub(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
58/// [`BlockchainReadRequest::BlockExtendedHeader`].
59pub(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
77/// [`BlockchainReadRequest::BlockHash`].
78pub(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
98/// [`BlockchainReadRequest::FindBlock`].
99pub(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
115/// [`BlockchainReadRequest::FilterUnknownHashes`].
116pub(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
132/// [`BlockchainReadRequest::BlockExtendedHeaderInRange`]
133pub(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
152/// [`BlockchainReadRequest::ChainHeight`].
153pub(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
168/// [`BlockchainReadRequest::GeneratedCoins`].
169pub(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
187/// [`BlockchainReadRequest::Outputs`]
188pub(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
204/// [`BlockchainReadRequest::NumberOutputsWithAmount`]
205pub(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
223/// [`BlockchainReadRequest::KeyImagesSpent`]
224pub(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
240/// [`BlockchainReadRequest::CompactChainHistory`]
241pub(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
259/// [`BlockchainReadRequest::FindFirstUnknown`]
260pub(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
276/// [`BlockchainReadRequest::TotalTxCount`]
277pub(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
292/// [`BlockchainReadRequest::DatabaseSize`]
293pub(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
311/// [`BlockchainReadRequest::OutputHistogram`]
312pub(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
328/// [`BlockchainReadRequest::CoinbaseTxSum`]
329pub(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
349/// [`BlockchainReadRequest::AltChains`]
350pub(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
365/// [`BlockchainReadRequest::AltChainCount`]
366pub(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}