cuprate_blockchain/ops/alt_block/
chain.rs1use std::cmp::{max, min};
2
3use cuprate_database::{DatabaseRo, DatabaseRw, DbResult, RuntimeError};
4use cuprate_types::{Chain, ChainId};
5
6use crate::{
7 ops::macros::{doc_add_alt_block_inner_invariant, doc_error},
8 tables::{AltChainInfos, TablesMut},
9 types::{AltBlockHeight, AltChainInfo, BlockHash, BlockHeight},
10};
11
12#[doc = doc_add_alt_block_inner_invariant!()]
15#[doc = doc_error!()]
16pub fn update_alt_chain_info(
21 alt_block_height: &AltBlockHeight,
22 prev_hash: &BlockHash,
23 tables: &mut impl TablesMut,
24) -> DbResult<()> {
25 let parent_chain = match tables.alt_block_heights().get(prev_hash) {
26 Ok(alt_parent_height) => Chain::Alt(alt_parent_height.chain_id.into()),
27 Err(RuntimeError::KeyNotFound) => Chain::Main,
28 Err(e) => return Err(e),
29 };
30
31 let update = tables
33 .alt_chain_infos_mut()
34 .update(&alt_block_height.chain_id, |mut info| {
35 if info.chain_height < alt_block_height.height + 1 {
36 info.chain_height = alt_block_height.height + 1;
38 } else {
39 info.common_ancestor_height = alt_block_height.height.checked_sub(1).unwrap();
42 info.parent_chain = parent_chain.into();
43 }
44
45 info.chain_height = alt_block_height.height + 1;
46 Some(info)
47 });
48
49 match update {
50 Ok(()) => return Ok(()),
51 Err(RuntimeError::KeyNotFound) => (),
52 Err(e) => return Err(e),
53 }
54
55 tables.alt_chain_infos_mut().put(
58 &alt_block_height.chain_id,
59 &AltChainInfo {
60 parent_chain: parent_chain.into(),
61 common_ancestor_height: alt_block_height.height.checked_sub(1).unwrap(),
62 chain_height: alt_block_height.height + 1,
63 },
64 )
65}
66
67#[doc = doc_error!()]
73pub fn get_alt_chain_history_ranges(
74 range: std::ops::Range<BlockHeight>,
75 alt_chain: ChainId,
76 alt_chain_infos: &impl DatabaseRo<AltChainInfos>,
77) -> DbResult<Vec<(Chain, std::ops::Range<BlockHeight>)>> {
78 let mut ranges = Vec::with_capacity(5);
79
80 let mut i = range.end;
81 let mut current_chain_id = alt_chain.into();
82 while i > range.start {
83 let chain_info = alt_chain_infos.get(¤t_chain_id)?;
84
85 let start_height = max(range.start, chain_info.common_ancestor_height + 1);
86 let end_height = min(i, chain_info.chain_height);
87
88 ranges.push((
89 Chain::Alt(current_chain_id.into()),
90 start_height..end_height,
91 ));
92 i = chain_info.common_ancestor_height + 1;
93
94 match chain_info.parent_chain.into() {
95 Chain::Main => {
96 ranges.push((Chain::Main, range.start..i));
97 break;
98 }
99 Chain::Alt(alt_chain_id) => {
100 let alt_chain_id = alt_chain_id.into();
101
102 if alt_chain_id == current_chain_id {
105 return Err(RuntimeError::Io(std::io::Error::other(
106 "Loop detected in ChainIDs, invalid alt chain.",
107 )));
108 }
109
110 current_chain_id = alt_chain_id;
111 continue;
112 }
113 }
114 }
115
116 Ok(ranges)
117}