cuprate_consensus_context/
alt_chains.rs1use std::{collections::HashMap, sync::Arc};
2
3use tower::ServiceExt;
4
5use cuprate_consensus_rules::{blocks::BlockError, ConsensusError};
6use cuprate_types::{
7 blockchain::{BlockchainReadRequest, BlockchainResponse},
8 Chain, ChainId,
9};
10
11use crate::{
12 ContextCacheError, __private::Database, difficulty::DifficultyCache, rx_vms::RandomXVm,
13 weight::BlockWeightsCache,
14};
15
16pub(crate) mod sealed {
17 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
22 pub struct AltChainRequestToken;
23}
24
25#[derive(Debug, Clone)]
27pub struct AltChainContextCache {
28 pub weight_cache: Option<BlockWeightsCache>,
30 pub difficulty_cache: Option<DifficultyCache>,
32
33 pub cached_rx_vm: Option<(usize, Arc<RandomXVm>)>,
35
36 pub chain_height: usize,
38 pub top_hash: [u8; 32],
40 pub chain_id: Option<ChainId>,
42 pub parent_chain: Chain,
44}
45
46impl AltChainContextCache {
47 pub fn add_new_block(
49 &mut self,
50 height: usize,
51 block_hash: [u8; 32],
52 block_weight: usize,
53 long_term_block_weight: usize,
54 timestamp: u64,
55 cumulative_difficulty: u128,
56 ) {
57 if let Some(difficulty_cache) = &mut self.difficulty_cache {
58 difficulty_cache.new_block(height, timestamp, cumulative_difficulty);
59 }
60
61 if let Some(weight_cache) = &mut self.weight_cache {
62 weight_cache.new_block(height, block_weight, long_term_block_weight);
63 }
64
65 self.chain_height += 1;
66 self.top_hash = block_hash;
67 }
68}
69
70pub(crate) struct AltChainMap {
72 alt_cache_map: HashMap<[u8; 32], Box<AltChainContextCache>>,
73}
74
75impl AltChainMap {
76 pub(crate) fn new() -> Self {
77 Self {
78 alt_cache_map: HashMap::new(),
79 }
80 }
81
82 pub(crate) fn clear(&mut self) {
83 self.alt_cache_map.clear();
84 }
85
86 pub(crate) fn add_alt_cache(&mut self, alt_cache: Box<AltChainContextCache>) {
88 self.alt_cache_map.insert(alt_cache.top_hash, alt_cache);
89 }
90
91 pub(crate) async fn get_alt_chain_context<D: Database>(
94 &mut self,
95 prev_id: [u8; 32],
96 database: D,
97 ) -> Result<Box<AltChainContextCache>, ContextCacheError> {
98 if let Some(cache) = self.alt_cache_map.remove(&prev_id) {
99 return Ok(cache);
100 }
101
102 let BlockchainResponse::FindBlock(res) = database
104 .oneshot(BlockchainReadRequest::FindBlock(prev_id))
105 .await?
106 else {
107 panic!("Database returned wrong response");
108 };
109
110 let Some((parent_chain, top_height)) = res else {
111 return Err(ConsensusError::Block(BlockError::PreviousIDIncorrect).into());
113 };
114
115 Ok(Box::new(AltChainContextCache {
116 weight_cache: None,
117 difficulty_cache: None,
118 cached_rx_vm: None,
119 chain_height: top_height + 1,
120 top_hash: prev_id,
121 chain_id: None,
122 parent_chain,
123 }))
124 }
125}
126
127pub(crate) async fn get_alt_chain_difficulty_cache<D: Database + Clone>(
129 prev_id: [u8; 32],
130 main_chain_difficulty_cache: &DifficultyCache,
131 mut database: D,
132) -> Result<DifficultyCache, ContextCacheError> {
133 let BlockchainResponse::FindBlock(res) = database
135 .ready()
136 .await?
137 .call(BlockchainReadRequest::FindBlock(prev_id))
138 .await?
139 else {
140 panic!("Database returned wrong response");
141 };
142
143 let Some((chain, top_height)) = res else {
144 return Err(ConsensusError::Block(BlockError::PreviousIDIncorrect).into());
146 };
147
148 Ok(match chain {
149 Chain::Main => {
150 let mut difficulty_cache = main_chain_difficulty_cache.clone();
152 difficulty_cache
153 .pop_blocks_main_chain(
154 difficulty_cache.last_accounted_height - top_height,
155 database,
156 )
157 .await?;
158
159 difficulty_cache
160 }
161 Chain::Alt(_) => {
162 DifficultyCache::init_from_chain_height(
164 top_height + 1,
165 main_chain_difficulty_cache.config,
166 database,
167 chain,
168 )
169 .await?
170 }
171 })
172}
173
174pub(crate) async fn get_alt_chain_weight_cache<D: Database + Clone>(
176 prev_id: [u8; 32],
177 main_chain_weight_cache: &BlockWeightsCache,
178 mut database: D,
179) -> Result<BlockWeightsCache, ContextCacheError> {
180 let BlockchainResponse::FindBlock(res) = database
182 .ready()
183 .await?
184 .call(BlockchainReadRequest::FindBlock(prev_id))
185 .await?
186 else {
187 panic!("Database returned wrong response");
188 };
189
190 let Some((chain, top_height)) = res else {
191 return Err(ConsensusError::Block(BlockError::PreviousIDIncorrect).into());
193 };
194
195 Ok(match chain {
196 Chain::Main => {
197 let mut weight_cache = main_chain_weight_cache.clone();
199 weight_cache
200 .pop_blocks_main_chain(weight_cache.tip_height - top_height, database)
201 .await?;
202
203 weight_cache
204 }
205 Chain::Alt(_) => {
206 BlockWeightsCache::init_from_chain_height(
208 top_height + 1,
209 main_chain_weight_cache.config,
210 database,
211 chain,
212 )
213 .await?
214 }
215 })
216}