cuprated/rpc/handlers/
helper.rs
1use anyhow::{anyhow, Error};
9
10use cuprate_helper::{
11 cast::{u64_to_usize, usize_to_u64},
12 map::split_u128_into_low_high_bits,
13};
14use cuprate_rpc_types::{
15 base::{AccessResponseBase, ResponseBase},
16 misc::BlockHeader,
17};
18use cuprate_types::HardFork;
19use monero_serai::transaction::Timelock;
20
21use crate::rpc::{
22 service::{blockchain, blockchain_context},
23 CupratedRpcHandler,
24};
25
26pub(super) async fn block_header(
31 state: &mut CupratedRpcHandler,
32 height: u64,
33 fill_pow_hash: bool,
34) -> Result<BlockHeader, Error> {
35 let block = blockchain::block(&mut state.blockchain_read, height).await?;
36 let header = blockchain::block_extended_header(&mut state.blockchain_read, height).await?;
37 let hardfork = HardFork::from_vote(header.vote);
38 let (top_height, _) = top_height(state).await?;
39
40 let orphan_status = false;
43
44 let difficulty = blockchain_context::batch_get_difficulties(
47 &mut state.blockchain_context,
48 vec![(height, hardfork)],
49 )
50 .await?
51 .first()
52 .copied()
53 .ok_or_else(|| anyhow!("Failed to get block difficulty"))?;
54
55 let pow_hash = if fill_pow_hash {
56 let seed_height =
57 cuprate_consensus_rules::blocks::randomx_seed_height(u64_to_usize(height));
58 let seed_hash = blockchain::block_hash(
59 &mut state.blockchain_read,
60 height,
61 todo!("access to `cuprated`'s Chain"),
62 )
63 .await?;
64
65 Some(
66 blockchain_context::calculate_pow(
67 &mut state.blockchain_context,
68 hardfork,
69 block,
70 seed_hash,
71 )
72 .await?,
73 )
74 } else {
75 None
76 };
77
78 let block_weight = usize_to_u64(header.block_weight);
79 let depth = top_height.saturating_sub(height);
80
81 let (cumulative_difficulty_top64, cumulative_difficulty) =
82 split_u128_into_low_high_bits(header.cumulative_difficulty);
83 let (difficulty_top64, difficulty) = split_u128_into_low_high_bits(difficulty);
84
85 let reward = block
86 .miner_transaction
87 .prefix()
88 .outputs
89 .iter()
90 .map(|o| o.amount.expect("coinbase is transparent"))
91 .sum::<u64>();
92
93 Ok(cuprate_types::rpc::BlockHeader {
94 block_weight,
95 cumulative_difficulty_top64,
96 cumulative_difficulty,
97 depth,
98 difficulty_top64,
99 difficulty,
100 hash: block.hash(),
101 height,
102 long_term_weight: usize_to_u64(header.long_term_weight),
103 major_version: header.version,
104 miner_tx_hash: block.miner_transaction.hash(),
105 minor_version: header.vote,
106 nonce: block.header.nonce,
107 num_txes: usize_to_u64(block.transactions.len()),
108 orphan_status,
109 pow_hash,
110 prev_hash: block.header.previous,
111 reward,
112 timestamp: block.header.timestamp,
113 }
114 .into())
115}
116
117pub(super) async fn block_header_by_hash(
119 state: &mut CupratedRpcHandler,
120 hash: [u8; 32],
121 fill_pow_hash: bool,
122) -> Result<BlockHeader, Error> {
123 let (_, height) = blockchain::find_block(&mut state.blockchain_read, hash)
124 .await?
125 .ok_or_else(|| anyhow!("Block did not exist."))?;
126
127 let block_header = block_header(state, usize_to_u64(height), fill_pow_hash).await?;
128
129 Ok(block_header)
130}
131
132pub(super) async fn check_height(
138 state: &mut CupratedRpcHandler,
139 height: u64,
140) -> Result<u64, Error> {
141 let (top_height, _) = top_height(state).await?;
142
143 if height > top_height {
144 return Err(anyhow!(
145 "Requested block height: {height} greater than top block height: {top_height}",
146 ));
147 }
148
149 Ok(top_height)
150}
151
152#[expect(clippy::needless_pass_by_value)]
154pub(super) fn hex_to_hash(hex: String) -> Result<[u8; 32], Error> {
155 let error = || anyhow!("Failed to parse hex representation of hash. Hex = {hex}.");
156
157 let Ok(bytes) = hex::decode(&hex) else {
158 return Err(error());
159 };
160
161 let Ok(hash) = bytes.try_into() else {
162 return Err(error());
163 };
164
165 Ok(hash)
166}
167
168pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [u8; 32]), Error> {
170 let (chain_height, hash) = blockchain::chain_height(&mut state.blockchain_read).await?;
171 let height = chain_height.checked_sub(1).unwrap();
172 Ok((height, hash))
173}
174
175pub const fn response_base(is_bootstrap: bool) -> ResponseBase {
177 if is_bootstrap {
178 ResponseBase::OK_UNTRUSTED
179 } else {
180 ResponseBase::OK
181 }
182}
183
184pub const fn access_response_base(is_bootstrap: bool) -> AccessResponseBase {
186 if is_bootstrap {
187 AccessResponseBase::OK_UNTRUSTED
188 } else {
189 AccessResponseBase::OK
190 }
191}