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},
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(get_blocks(state, r).await?),
48 Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(get_blocks_by_height(state, r).await?),
49 Req::GetHashes(r) => Resp::GetHashes(get_hashes(state, r).await?),
50 Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(get_output_indexes(state, r).await?),
51 Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?),
52 Req::GetTransactionPoolHashes(r) => {
53 Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?)
54 }
55 Req::GetOutputDistribution(r) => {
56 Resp::GetOutputDistribution(get_output_distribution(state, r).await?)
57 }
58 })
59}
60
61async fn get_blocks(
63 mut state: CupratedRpcHandler,
64 request: GetBlocksRequest,
65) -> Result<GetBlocksResponse, Error> {
66 let daemon_time = cuprate_helper::time::current_unix_timestamp();
69
70 let GetBlocksRequest {
71 requested_info,
72 block_ids,
73 start_height,
74 prune,
75 no_miner_tx,
76 pool_info_since,
77 } = request;
78
79 let block_hashes: Vec<[u8; 32]> = (&block_ids).into();
80 drop(block_ids);
81
82 let Some(requested_info) = RequestedInfo::from_u8(request.requested_info) else {
83 return Err(anyhow!("Wrong requested info"));
84 };
85
86 let (get_blocks, get_pool) = match requested_info {
87 RequestedInfo::BlocksOnly => (true, false),
88 RequestedInfo::BlocksAndPool => (true, true),
89 RequestedInfo::PoolOnly => (false, true),
90 };
91
92 let pool_info_extent = PoolInfoExtent::None;
93
94 let pool_info = if get_pool {
95 let is_restricted = state.is_restricted();
96 let include_sensitive_txs = !is_restricted;
97
98 let max_tx_count = if is_restricted {
99 RESTRICTED_TRANSACTIONS_COUNT
100 } else {
101 usize::MAX
102 };
103
104 txpool::pool_info(
105 &mut state.txpool_read,
106 include_sensitive_txs,
107 max_tx_count,
108 NonZero::new(u64_to_usize(request.pool_info_since)),
109 )
110 .await?
111 } else {
112 PoolInfo::None
113 };
114
115 let resp = GetBlocksResponse {
116 base: helper::access_response_base(false),
117 blocks: vec![],
118 start_height: 0,
119 current_height: 0,
120 output_indices: vec![],
121 daemon_time,
122 pool_info,
123 };
124
125 if !get_blocks {
126 return Ok(resp);
127 }
128
129 if let Some(block_id) = block_hashes.first() {
130 let (height, hash) = helper::top_height(&mut state).await?;
131
132 if hash == *block_id {
133 return Ok(GetBlocksResponse {
134 current_height: height + 1,
135 ..resp
136 });
137 }
138 }
139
140 let (block_hashes, start_height, _) =
141 blockchain::next_chain_entry(&mut state.blockchain_read, block_hashes, start_height)
142 .await?;
143
144 if start_height.is_none() {
145 return Err(anyhow!("Block IDs were not sorted properly"));
146 }
147
148 let (blocks, missing_hashes, height) =
149 blockchain::block_complete_entries(&mut state.blockchain_read, block_hashes).await?;
150
151 if !missing_hashes.is_empty() {
152 return Err(anyhow!("Missing blocks"));
153 }
154
155 Ok(GetBlocksResponse {
156 blocks,
157 current_height: usize_to_u64(height),
158 ..resp
159 })
160}
161
162async fn get_blocks_by_height(
164 mut state: CupratedRpcHandler,
165 request: GetBlocksByHeightRequest,
166) -> Result<GetBlocksByHeightResponse, Error> {
167 if state.is_restricted() && request.heights.len() > RESTRICTED_BLOCK_COUNT {
168 return Err(anyhow!("Too many blocks requested in restricted mode"));
169 }
170
171 let blocks =
172 blockchain::block_complete_entries_by_height(&mut state.blockchain_read, request.heights)
173 .await?;
174
175 Ok(GetBlocksByHeightResponse {
176 base: helper::access_response_base(false),
177 blocks,
178 })
179}
180
181async fn get_hashes(
183 mut state: CupratedRpcHandler,
184 request: GetHashesRequest,
185) -> Result<GetHashesResponse, Error> {
186 let GetHashesRequest {
187 start_height,
188 block_ids,
189 } = request;
190
191 let last = {
193 let len = block_ids.len();
194
195 if len == 0 {
196 return Err(anyhow!("block_ids empty"));
197 }
198
199 block_ids[len - 1]
200 };
201
202 let hashes: Vec<[u8; 32]> = (&block_ids).into();
203
204 let (m_blocks_ids, _, current_height) =
205 blockchain::next_chain_entry(&mut state.blockchain_read, hashes, start_height).await?;
206
207 Ok(GetHashesResponse {
208 base: helper::access_response_base(false),
209 m_blocks_ids: m_blocks_ids.into(),
210 current_height: usize_to_u64(current_height),
211 start_height,
212 })
213}
214
215async fn get_output_indexes(
217 mut state: CupratedRpcHandler,
218 request: GetOutputIndexesRequest,
219) -> Result<GetOutputIndexesResponse, Error> {
220 Ok(GetOutputIndexesResponse {
221 base: helper::access_response_base(false),
222 o_indexes: blockchain::tx_output_indexes(&mut state.blockchain_read, request.txid).await?,
223 })
224}
225
226async fn get_outs(
228 state: CupratedRpcHandler,
229 request: GetOutsRequest,
230) -> Result<GetOutsResponse, Error> {
231 shared::get_outs(state, request).await
232}
233
234async fn get_transaction_pool_hashes(
236 mut state: CupratedRpcHandler,
237 _: GetTransactionPoolHashesRequest,
238) -> Result<GetTransactionPoolHashesResponse, Error> {
239 Ok(GetTransactionPoolHashesResponse {
240 base: helper::access_response_base(false),
241 tx_hashes: shared::get_transaction_pool_hashes(state)
242 .await
243 .map(ByteArrayVec::from)?,
244 })
245}
246
247async fn get_output_distribution(
249 state: CupratedRpcHandler,
250 request: GetOutputDistributionRequest,
251) -> Result<GetOutputDistributionResponse, Error> {
252 shared::get_output_distribution(state, request).await
253}