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::{Chain, 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 =
59 blockchain::block_hash(&mut state.blockchain_read, height, Chain::Main).await?;
60
61 Some(
62 blockchain_context::calculate_pow(
63 &mut state.blockchain_context,
64 hardfork,
65 block.clone(),
67 seed_hash,
68 )
69 .await?,
70 )
71 } else {
72 None
73 };
74
75 let block_weight = usize_to_u64(header.block_weight);
76 let depth = top_height.saturating_sub(height);
77
78 let (cumulative_difficulty_top64, cumulative_difficulty) =
79 split_u128_into_low_high_bits(header.cumulative_difficulty);
80 let (difficulty_top64, difficulty) = split_u128_into_low_high_bits(difficulty);
81
82 let reward = block
83 .miner_transaction
84 .prefix()
85 .outputs
86 .iter()
87 .map(|o| o.amount.expect("coinbase is transparent"))
88 .sum::<u64>();
89
90 Ok(cuprate_types::rpc::BlockHeader {
91 block_weight,
92 cumulative_difficulty_top64,
93 cumulative_difficulty,
94 depth,
95 difficulty_top64,
96 difficulty,
97 hash: block.hash(),
98 height,
99 long_term_weight: usize_to_u64(header.long_term_weight),
100 major_version: header.version,
101 miner_tx_hash: block.miner_transaction.hash(),
102 minor_version: header.vote,
103 nonce: block.header.nonce,
104 num_txes: usize_to_u64(block.transactions.len()),
105 orphan_status,
106 pow_hash,
107 prev_hash: block.header.previous,
108 reward,
109 timestamp: block.header.timestamp,
110 }
111 .into())
112}
113
114pub(super) async fn block_header_by_hash(
116 state: &mut CupratedRpcHandler,
117 hash: [u8; 32],
118 fill_pow_hash: bool,
119) -> Result<BlockHeader, Error> {
120 let (_, height) = blockchain::find_block(&mut state.blockchain_read, hash)
121 .await?
122 .ok_or_else(|| anyhow!("Block did not exist."))?;
123
124 let block_header = block_header(state, usize_to_u64(height), fill_pow_hash).await?;
125
126 Ok(block_header)
127}
128
129pub(super) async fn check_height(
135 state: &mut CupratedRpcHandler,
136 height: u64,
137) -> Result<u64, Error> {
138 let (top_height, _) = top_height(state).await?;
139
140 if height > top_height {
141 return Err(anyhow!(
142 "Requested block height: {height} greater than top block height: {top_height}",
143 ));
144 }
145
146 Ok(top_height)
147}
148
149#[expect(clippy::needless_pass_by_value)]
151pub(super) fn hex_to_hash(hex: String) -> Result<[u8; 32], Error> {
152 let error = || anyhow!("Failed to parse hex representation of hash. Hex = {hex}.");
153
154 let Ok(bytes) = hex::decode(&hex) else {
155 return Err(error());
156 };
157
158 let Ok(hash) = bytes.try_into() else {
159 return Err(error());
160 };
161
162 Ok(hash)
163}
164
165pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [u8; 32]), Error> {
167 let (chain_height, hash) = blockchain::chain_height(&mut state.blockchain_read).await?;
168 let height = chain_height.checked_sub(1).unwrap();
169 Ok((height, hash))
170}
171
172pub const fn response_base(is_bootstrap: bool) -> ResponseBase {
174 if is_bootstrap {
175 ResponseBase::OK_UNTRUSTED
176 } else {
177 ResponseBase::OK
178 }
179}
180
181pub const fn access_response_base(is_bootstrap: bool) -> AccessResponseBase {
183 if is_bootstrap {
184 AccessResponseBase::OK_UNTRUSTED
185 } else {
186 AccessResponseBase::OK
187 }
188}