cuprate_blockchain/ops/alt_block/
chain.rs1use std::cmp::{max, min};
2
3use fjall::Readable;
4
5use cuprate_types::{Chain, ChainId};
6
7use crate::{
8 error::{BlockchainError, DbResult},
9 types::{AltBlockHeight, AltChainInfo, BlockHash, BlockHeight, RawChainId},
10 BlockchainDatabase,
11};
12
13pub fn update_alt_chain_info(
22 db: &BlockchainDatabase,
23 alt_block_height: &AltBlockHeight,
24 prev_hash: &BlockHash,
25) -> DbResult<()> {
26 let parent_chain = match db.alt_block_heights.load().get(prev_hash) {
27 Ok(Some(alt_parent_height)) => {
28 let alt_parent_height: AltBlockHeight =
29 bytemuck::pod_read_unaligned(alt_parent_height.as_ref());
30 Chain::Alt(alt_parent_height.chain_id.into())
31 }
32 Ok(None) => Chain::Main,
33 Err(e) => return Err(e.into()),
34 };
35
36 let Some(info) = db
37 .alt_chain_infos
38 .load()
39 .get(alt_block_height.chain_id.0.to_le_bytes())?
40 else {
41 db.alt_chain_infos.load().insert(
42 alt_block_height.chain_id.0.to_le_bytes(),
43 bytemuck::bytes_of(&AltChainInfo {
44 parent_chain: parent_chain.into(),
45 common_ancestor_height: alt_block_height.height.checked_sub(1).unwrap(),
46 chain_height: alt_block_height.height + 1,
47 }),
48 )?;
49
50 return Ok(());
51 };
52
53 let mut info: AltChainInfo = bytemuck::pod_read_unaligned(info.as_ref());
54
55 if info.chain_height < alt_block_height.height + 1 {
56 info.chain_height = alt_block_height.height + 1;
58 } else {
59 info.common_ancestor_height = alt_block_height.height.checked_sub(1).unwrap();
62 info.parent_chain = parent_chain.into();
63 }
64
65 db.alt_chain_infos.load().insert(
66 alt_block_height.chain_id.0.to_le_bytes(),
67 bytemuck::bytes_of(&info),
68 )?;
69
70 Ok(())
71}
72
73pub fn get_alt_chain_history_ranges(
80 db: &BlockchainDatabase,
81 range: std::ops::Range<BlockHeight>,
82 alt_chain: ChainId,
83 tx_ro: &fjall::Snapshot,
84) -> DbResult<Vec<(Chain, std::ops::Range<BlockHeight>)>> {
85 let mut ranges = Vec::with_capacity(5);
86
87 let mut i = range.end;
88 let mut current_chain_id: RawChainId = alt_chain.into();
89 while i > range.start {
90 let chain_info = tx_ro
91 .get(
92 &**db.alt_chain_infos.load(),
93 current_chain_id.0.to_le_bytes(),
94 )?
95 .ok_or(BlockchainError::NotFound)?;
96
97 let chain_info: AltChainInfo = bytemuck::pod_read_unaligned(chain_info.as_ref());
98
99 let start_height = max(range.start, chain_info.common_ancestor_height + 1);
100 let end_height = min(i, chain_info.chain_height);
101
102 ranges.push((
103 Chain::Alt(current_chain_id.into()),
104 start_height..end_height,
105 ));
106 i = chain_info.common_ancestor_height + 1;
107
108 match chain_info.parent_chain.into() {
109 Chain::Main => {
110 ranges.push((Chain::Main, range.start..i));
111 break;
112 }
113 Chain::Alt(alt_chain_id) => {
114 let alt_chain_id = alt_chain_id.into();
115
116 if alt_chain_id == current_chain_id {
119 return Err(BlockchainError::IO(std::io::Error::other(
120 "Loop detected in ChainIDs, invalid alt chain.",
121 )));
122 }
123
124 current_chain_id = alt_chain_id;
125 continue;
126 }
127 }
128 }
129
130 Ok(ranges)
131}