1use std::num::NonZero;
8
9use anyhow::{anyhow, Error};
10use bytes::Bytes;
11
12use cuprate_constants::rpc::{RESTRICTED_BLOCK_COUNT, RESTRICTED_TRANSACTIONS_COUNT};
13use cuprate_fixed_bytes::ByteArrayVec;
14use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
15use cuprate_rpc_interface::RpcHandler;
16use cuprate_rpc_types::{
17 base::{AccessResponseBase, ResponseBase},
18 bin::{
19 BinRequest, BinResponse, GetBlocksByHeightRequest, GetBlocksByHeightResponse,
20 GetBlocksRequest, GetBlocksResponse, GetHashesRequest, GetHashesResponse,
21 GetOutputIndexesRequest, GetOutputIndexesResponse, GetOutsRequest, GetOutsResponse,
22 GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
23 },
24 json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
25 misc::RequestedInfo,
26};
27use cuprate_types::{
28 rpc::{PoolInfo, PoolInfoExtent},
29 BlockCompleteEntry,
30};
31
32use crate::rpc::{
33 handlers::{helper, shared, shared::not_available},
34 service::{blockchain, txpool},
35 CupratedRpcHandler,
36};
37
38pub async fn map_request(
40 state: CupratedRpcHandler,
41 request: BinRequest,
42) -> Result<BinResponse, Error> {
43 use BinRequest as Req;
44 use BinResponse as Resp;
45
46 Ok(match request {
47 Req::GetBlocks(r) => Resp::GetBlocks(not_available()?),
48 Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(not_available()?),
49 Req::GetHashes(r) => Resp::GetHashes(not_available()?),
50 Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(not_available()?),
51 Req::GetOuts(r) => Resp::GetOuts(not_available()?),
52 Req::GetTransactionPoolHashes(r) => Resp::GetTransactionPoolHashes(not_available()?),
53 Req::GetOutputDistribution(r) => Resp::GetOutputDistribution(not_available()?),
54 })
55}
56
57async fn get_blocks(
59 mut state: CupratedRpcHandler,
60 request: GetBlocksRequest,
61) -> Result<GetBlocksResponse, Error> {
62 let daemon_time = cuprate_helper::time::current_unix_timestamp();
65
66 let GetBlocksRequest {
67 requested_info,
68 block_ids,
69 start_height,
70 prune,
71 no_miner_tx,
72 pool_info_since,
73 } = request;
74
75 let block_hashes: Vec<[u8; 32]> = (&block_ids).into();
76 drop(block_ids);
77
78 let Some(requested_info) = RequestedInfo::from_u8(request.requested_info) else {
79 return Err(anyhow!("Wrong requested info"));
80 };
81
82 let (get_blocks, get_pool) = match requested_info {
83 RequestedInfo::BlocksOnly => (true, false),
84 RequestedInfo::BlocksAndPool => (true, true),
85 RequestedInfo::PoolOnly => (false, true),
86 };
87
88 let pool_info_extent = PoolInfoExtent::None;
89
90 let pool_info = if get_pool {
91 let is_restricted = state.is_restricted();
92 let include_sensitive_txs = !is_restricted;
93
94 let max_tx_count = if is_restricted {
95 RESTRICTED_TRANSACTIONS_COUNT
96 } else {
97 usize::MAX
98 };
99
100 txpool::pool_info(
101 &mut state.txpool_read,
102 include_sensitive_txs,
103 max_tx_count,
104 NonZero::new(u64_to_usize(request.pool_info_since)),
105 )
106 .await?
107 } else {
108 PoolInfo::None
109 };
110
111 let resp = GetBlocksResponse {
112 base: helper::access_response_base(false),
113 blocks: vec![],
114 start_height: 0,
115 current_height: 0,
116 output_indices: vec![],
117 daemon_time,
118 pool_info,
119 };
120
121 if !get_blocks {
122 return Ok(resp);
123 }
124
125 if let Some(block_id) = block_hashes.first() {
126 let (height, hash) = helper::top_height(&mut state).await?;
127
128 if hash == *block_id {
129 return Ok(GetBlocksResponse {
130 current_height: height + 1,
131 ..resp
132 });
133 }
134 }
135
136 let (block_hashes, start_height, _) =
137 blockchain::next_chain_entry(&mut state.blockchain_read, block_hashes, start_height)
138 .await?;
139
140 if start_height.is_none() {
141 return Err(anyhow!("Block IDs were not sorted properly"));
142 }
143
144 let (blocks, missing_hashes, height) =
145 blockchain::block_complete_entries(&mut state.blockchain_read, block_hashes).await?;
146
147 if !missing_hashes.is_empty() {
148 return Err(anyhow!("Missing blocks"));
149 }
150
151 Ok(GetBlocksResponse {
152 blocks,
153 current_height: usize_to_u64(height),
154 ..resp
155 })
156}
157
158async fn get_blocks_by_height(
160 mut state: CupratedRpcHandler,
161 request: GetBlocksByHeightRequest,
162) -> Result<GetBlocksByHeightResponse, Error> {
163 if state.is_restricted() && request.heights.len() > RESTRICTED_BLOCK_COUNT {
164 return Err(anyhow!("Too many blocks requested in restricted mode"));
165 }
166
167 let blocks =
168 blockchain::block_complete_entries_by_height(&mut state.blockchain_read, request.heights)
169 .await?;
170
171 Ok(GetBlocksByHeightResponse {
172 base: helper::access_response_base(false),
173 blocks,
174 })
175}
176
177async fn get_hashes(
179 mut state: CupratedRpcHandler,
180 request: GetHashesRequest,
181) -> Result<GetHashesResponse, Error> {
182 let GetHashesRequest {
183 start_height,
184 block_ids,
185 } = request;
186
187 let last = {
189 let len = block_ids.len();
190
191 if len == 0 {
192 return Err(anyhow!("block_ids empty"));
193 }
194
195 block_ids[len - 1]
196 };
197
198 let hashes: Vec<[u8; 32]> = (&block_ids).into();
199
200 let (m_blocks_ids, _, current_height) =
201 blockchain::next_chain_entry(&mut state.blockchain_read, hashes, start_height).await?;
202
203 Ok(GetHashesResponse {
204 base: helper::access_response_base(false),
205 m_blocks_ids: m_blocks_ids.into(),
206 current_height: usize_to_u64(current_height),
207 start_height,
208 })
209}
210
211async fn get_output_indexes(
213 mut state: CupratedRpcHandler,
214 request: GetOutputIndexesRequest,
215) -> Result<GetOutputIndexesResponse, Error> {
216 Ok(GetOutputIndexesResponse {
217 base: helper::access_response_base(false),
218 o_indexes: blockchain::tx_output_indexes(&mut state.blockchain_read, request.txid).await?,
219 })
220}
221
222async fn get_outs(
224 state: CupratedRpcHandler,
225 request: GetOutsRequest,
226) -> Result<GetOutsResponse, Error> {
227 shared::get_outs(state, request).await
228}
229
230async fn get_transaction_pool_hashes(
232 mut state: CupratedRpcHandler,
233 _: GetTransactionPoolHashesRequest,
234) -> Result<GetTransactionPoolHashesResponse, Error> {
235 Ok(GetTransactionPoolHashesResponse {
236 base: helper::access_response_base(false),
237 tx_hashes: shared::get_transaction_pool_hashes(state)
238 .await
239 .map(ByteArrayVec::from)?,
240 })
241}
242
243async fn get_output_distribution(
245 state: CupratedRpcHandler,
246 request: GetOutputDistributionRequest,
247) -> Result<GetOutputDistributionResponse, Error> {
248 shared::get_output_distribution(state, request).await
249}