cuprate_blockchain/ops/alt_block/
block.rs1use fjall::Readable;
2use monero_oxide::block::{Block, BlockHeader};
3
4use cuprate_helper::{
5 cast::usize_to_u64,
6 map::{combine_low_high_bits_to_u128, split_u128_into_low_high_bits},
7};
8use cuprate_types::{AltBlockInformation, Chain, ChainId, ExtendedBlockHeader, HardFork};
9
10use crate::{
11 database::reset_fjall_keyspace,
12 error::{BlockchainError, DbResult},
13 ops::alt_block::{add_alt_transaction_blob, get_alt_transaction, update_alt_chain_info},
14 types::{
15 AltBlockHeight, AltChainInfo, BlockHash, BlockHeight, CompactAltBlockInfo, RawChainId,
16 },
17 BlockchainDatabase,
18};
19
20pub fn flush_alt_blocks(db: &BlockchainDatabase) -> DbResult<()> {
26 reset_fjall_keyspace(&db.fjall, &db.alt_chain_infos)?;
27 reset_fjall_keyspace(&db.fjall, &db.alt_block_heights)?;
28 reset_fjall_keyspace(&db.fjall, &db.alt_block_infos)?;
29 reset_fjall_keyspace(&db.fjall, &db.alt_block_blobs)?;
30 reset_fjall_keyspace(&db.fjall, &db.alt_transaction_blobs)?;
31 reset_fjall_keyspace(&db.fjall, &db.alt_transaction_infos)?;
32
33 Ok(())
34}
35
36pub fn add_alt_block(
46 db: &BlockchainDatabase,
47 alt_block: &AltBlockInformation,
48 tx_rw: &mut fjall::OwnedWriteBatch,
49) -> DbResult<()> {
50 let alt_block_height = AltBlockHeight {
51 chain_id: alt_block.chain_id.into(),
52 height: alt_block.height,
53 };
54
55 tx_rw.insert(
56 &db.alt_block_heights.load(),
57 alt_block.block_hash,
58 bytemuck::bytes_of(&alt_block_height),
59 );
60 update_alt_chain_info(db, &alt_block_height, &alt_block.block.header.previous)?;
61
62 let (cumulative_difficulty_low, cumulative_difficulty_high) =
63 split_u128_into_low_high_bits(alt_block.cumulative_difficulty);
64
65 let alt_block_info = CompactAltBlockInfo {
66 block_hash: alt_block.block_hash,
67 pow_hash: alt_block.pow_hash,
68 height: alt_block.height,
69 weight: alt_block.weight,
70 long_term_weight: alt_block.long_term_weight,
71 cumulative_difficulty_low,
72 cumulative_difficulty_high,
73 };
74
75 tx_rw.insert(
76 &db.alt_block_infos.load(),
77 bytemuck::bytes_of(&alt_block_height),
78 bytemuck::bytes_of(&alt_block_info),
79 );
80 tx_rw.insert(
81 &db.alt_block_blobs.load(),
82 bytemuck::bytes_of(&alt_block_height),
83 &alt_block.block_blob,
84 );
85
86 assert_eq!(alt_block.txs.len(), alt_block.block.transactions.len());
87 for tx in &alt_block.txs {
88 add_alt_transaction_blob(db, tx, tx_rw)?;
89 }
90
91 Ok(())
92}
93
94pub fn get_alt_block_information(
99 db: &BlockchainDatabase,
100 alt_block_height: &AltBlockHeight,
101 tx_ro: &fjall::Snapshot,
102) -> DbResult<AltBlockInformation> {
103 let block_info = tx_ro
104 .get(
105 &**db.alt_block_infos.load(),
106 bytemuck::bytes_of(alt_block_height),
107 )?
108 .ok_or(BlockchainError::NotFound)?;
109
110 let block_info: CompactAltBlockInfo = bytemuck::pod_read_unaligned(block_info.as_ref());
111
112 let block_blob = tx_ro
113 .get(
114 &**db.alt_block_blobs.load(),
115 bytemuck::bytes_of(alt_block_height),
116 )?
117 .ok_or(BlockchainError::NotFound)?;
118
119 let block = Block::read(&mut block_blob.as_ref()).unwrap();
120
121 let txs = block
122 .transactions
123 .iter()
124 .map(|tx_hash| get_alt_transaction(db, tx_hash, tx_ro))
125 .collect::<DbResult<Vec<_>>>()?;
126
127 Ok(AltBlockInformation {
128 block,
129 block_blob: block_blob.to_vec(),
130 txs,
131 block_hash: block_info.block_hash,
132 pow_hash: block_info.pow_hash,
133 height: block_info.height,
134 weight: block_info.weight,
135 long_term_weight: block_info.long_term_weight,
136 cumulative_difficulty: combine_low_high_bits_to_u128(
137 block_info.cumulative_difficulty_low,
138 block_info.cumulative_difficulty_high,
139 ),
140 chain_id: alt_block_height.chain_id.into(),
141 })
142}
143
144pub fn get_alt_block(
149 db: &BlockchainDatabase,
150 alt_block_height: &AltBlockHeight,
151 tx_ro: &fjall::Snapshot,
152) -> DbResult<Block> {
153 let block_blob = tx_ro
154 .get(
155 &**db.alt_block_blobs.load(),
156 bytemuck::bytes_of(alt_block_height),
157 )?
158 .ok_or(BlockchainError::NotFound)?;
159
160 Ok(Block::read(&mut block_blob.as_ref()).unwrap())
161}
162
163pub fn get_alt_block_hash(
170 db: &BlockchainDatabase,
171 block_height: &BlockHeight,
172 alt_chain: ChainId,
173 tx_ro: &fjall::Snapshot,
174 tapes: &impl tapes::TapesRead,
175) -> DbResult<BlockHash> {
176 let original_chain = {
178 let mut chain: RawChainId = alt_chain.into();
179 loop {
180 let chain_info = tx_ro
181 .get(&**db.alt_chain_infos.load(), chain.0.to_le_bytes())?
182 .ok_or(BlockchainError::NotFound)?;
183
184 let chain_info: AltChainInfo = bytemuck::pod_read_unaligned(chain_info.as_ref());
185
186 if chain_info.common_ancestor_height < *block_height {
187 break Chain::Alt(chain.into());
188 }
189
190 match chain_info.parent_chain.into() {
191 Chain::Main => break Chain::Main,
192 Chain::Alt(alt_chain_id) => {
193 chain = alt_chain_id.into();
194 continue;
195 }
196 }
197 }
198 };
199
200 match original_chain {
202 Chain::Main => tapes
203 .read_entry(&db.block_infos, usize_to_u64(*block_height))?
204 .map(|info| info.block_hash)
205 .ok_or(BlockchainError::NotFound),
206 Chain::Alt(chain_id) => tx_ro
207 .get(
208 &**db.alt_block_infos.load(),
209 bytemuck::bytes_of(&AltBlockHeight {
210 chain_id: chain_id.into(),
211 height: *block_height,
212 }),
213 )?
214 .map(|info| {
215 let info: CompactAltBlockInfo = bytemuck::pod_read_unaligned(info.as_ref());
216 info.block_hash
217 })
218 .ok_or(BlockchainError::NotFound),
219 }
220}
221
222pub fn get_alt_block_extended_header_from_height(
227 db: &BlockchainDatabase,
228 height: &AltBlockHeight,
229 tx_ro: &fjall::Snapshot,
230) -> DbResult<ExtendedBlockHeader> {
231 let block_info = tx_ro
232 .get(&**db.alt_block_infos.load(), bytemuck::bytes_of(height))?
233 .ok_or(BlockchainError::NotFound)?;
234
235 let block_info: CompactAltBlockInfo = bytemuck::pod_read_unaligned(block_info.as_ref());
236
237 let block_blob = tx_ro
238 .get(&**db.alt_block_blobs.load(), bytemuck::bytes_of(height))?
239 .ok_or(BlockchainError::NotFound)?;
240
241 let block_header = BlockHeader::read(&mut block_blob.as_ref())?;
242
243 Ok(ExtendedBlockHeader {
244 version: HardFork::from_version(block_header.hardfork_version)
245 .expect("Block in DB must have correct version"),
246 vote: block_header.hardfork_signal,
247 timestamp: block_header.timestamp,
248 cumulative_difficulty: combine_low_high_bits_to_u128(
249 block_info.cumulative_difficulty_low,
250 block_info.cumulative_difficulty_high,
251 ),
252 block_weight: block_info.weight,
253 long_term_weight: block_info.long_term_weight,
254 })
255}
256
257pub(crate) fn alt_block_height(
259 db: &BlockchainDatabase,
260 tx_ro: &fjall::Snapshot,
261 hash: &BlockHash,
262) -> DbResult<Option<AltBlockHeight>> {
263 let Some(bytes) = tx_ro.get(&**db.alt_block_heights.load(), hash)? else {
264 return Ok(None);
265 };
266
267 Ok(Some(bytemuck::pod_read_unaligned(bytes.as_ref())))
268}