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