cfxcore/consensus/consensus_inner/
confirmation_meter.rs1use crate::consensus::{
6 consensus_inner::{NULL, NULLU64},
7 ConsensusGraphInner,
8};
9use cfx_parameters::{
10 consensus::DEFERRED_STATE_EPOCH_COUNT, consensus_internal::*,
11};
12use cfx_types::H256;
13use parking_lot::RwLock;
14use std::{cmp::max, collections::VecDeque, convert::TryFrom};
15
16pub struct TotalWeightInPastMovingDelta {
17 pub old: i128,
18 pub cur: i128,
19 pub delta: i128,
20}
21
22pub struct FinalityManager {
23 pub lowest_epoch_num: u64,
24 pub risks_less_than: VecDeque<f64>,
25}
26
27struct ConfirmationMeterInner {
28 total_weight_in_past_2d: TotalWeightInPastMovingDelta,
29 finality_manager: FinalityManager,
30}
31
32impl ConfirmationMeterInner {
33 pub fn new() -> Self {
34 Self {
35 total_weight_in_past_2d: TotalWeightInPastMovingDelta {
36 old: 0,
37 cur: 0,
38 delta: 0,
39 },
40 finality_manager: FinalityManager {
41 lowest_epoch_num: 0,
42 risks_less_than: VecDeque::new(),
43 },
44 }
45 }
46}
47
48pub struct ConfirmationMeter {
59 inner: RwLock<ConfirmationMeterInner>,
60}
61
62impl ConfirmationMeter {
63 pub fn new() -> Self {
64 Self {
65 inner: RwLock::new(ConfirmationMeterInner::new()),
66 }
67 }
68
69 pub fn clear(&self) {
70 let mut inner = self.inner.write();
71 *inner = ConfirmationMeterInner::new();
72 }
73
74 pub fn update_total_weight_delta_heartbeat(&self) {
78 let mut inner = self.inner.write();
79 let total_weight = &mut inner.total_weight_in_past_2d;
80 total_weight.delta = total_weight.cur - total_weight.old;
81 total_weight.old = total_weight.cur;
82 }
83
84 pub fn aggregate_total_weight_in_past(&self, weight: i128) {
87 let mut inner = self.inner.write();
88 let total_weight = &mut inner.total_weight_in_past_2d;
89 total_weight.cur += weight;
90 }
91
92 pub fn reset_for_checkpoint(&self, total_weight: i128, stable_height: u64) {
96 let mut inner = self.inner.write();
97 let change = inner.total_weight_in_past_2d.cur - total_weight;
98 inner.total_weight_in_past_2d.cur = total_weight;
99 inner.total_weight_in_past_2d.old -= change;
100
101 if stable_height > inner.finality_manager.lowest_epoch_num {
102 let gap = stable_height - inner.finality_manager.lowest_epoch_num;
103 for _i in 0..gap {
104 inner.finality_manager.risks_less_than.pop_front();
105 }
106 inner.finality_manager.lowest_epoch_num = stable_height;
107 }
108 }
109
110 pub fn get_confirmed_epoch_num(&self) -> u64 {
111 let x = self.inner.read().finality_manager.lowest_epoch_num;
112 if x > 0 {
113 x - 1
114 } else {
115 0
116 }
117 }
118
119 pub fn confirmation_risk_by_hash(
121 &self, g_inner: &ConsensusGraphInner, hash: H256,
122 ) -> Option<f64> {
123 if hash == g_inner.data_man.true_genesis.hash() {
124 return Some(CONFIRMATION_METER_MIN_MAINTAINED_RISK);
125 }
126 let index = match g_inner.hash_to_arena_indices.get(&hash) {
127 Some(i) => *i,
128 None => {
129 return match g_inner
131 .data_man
132 .block_execution_result_by_hash_from_db(&hash)
133 {
134 Some(_) => Some(CONFIRMATION_METER_MIN_MAINTAINED_RISK),
138 None => None,
143 };
144 }
145 };
146 let epoch_num = g_inner.arena[index].data.epoch_number;
147 if epoch_num == NULLU64 {
148 return None;
153 }
154
155 if epoch_num == 0 {
156 return Some(0.0);
157 }
158
159 let finality = &self.inner.read().finality_manager;
160
161 if epoch_num < finality.lowest_epoch_num {
162 return Some(CONFIRMATION_METER_MIN_MAINTAINED_RISK);
163 }
164
165 let idx = (epoch_num - finality.lowest_epoch_num) as usize;
166 if idx < finality.risks_less_than.len() {
167 let mut max_risk = 0.0;
168 for i in 0..idx + 1 {
169 let risk = *finality.risks_less_than.get(i).unwrap();
170 if max_risk < risk {
171 max_risk = risk;
172 }
173 }
174 Some(max_risk)
175 } else {
176 Some(0.9)
177 }
178 }
179
180 fn confirmation_risk(
181 &self, g_inner: &ConsensusGraphInner, w_0: i128, w_4: i128,
182 epoch_num: u64,
183 ) -> f64 {
184 let idx = g_inner.get_pivot_block_arena_index(epoch_num);
186 let pivot_idx = g_inner.height_to_pivot_index(epoch_num);
187 let w_1 = g_inner.weight_tree.get(idx);
188
189 let parent = g_inner.arena[idx].parent;
191 assert!(parent != NULL);
192 let mut max_weight = 0;
193 for child in g_inner.arena[parent].children.iter() {
194 if *child == idx {
195 continue;
196 }
197
198 let child_weight = g_inner.weight_tree.get(*child);
199 if child_weight > max_weight {
200 max_weight = child_weight;
201 }
202 }
203 let w_2 = max_weight;
204
205 let w_3 = g_inner.pivot_chain_metadata[pivot_idx].past_weight;
207
208 let d = i128::try_from(g_inner.current_difficulty.low_u128()).unwrap();
210
211 let w_2_4 = w_2 + w_4;
213 let n = if w_1 >= w_2_4 { w_1 - w_2_4 } else { 0 };
214
215 let n = (n / d) + 1;
216
217 let m = if w_0 >= w_3 { w_0 - w_3 } else { 0 };
219
220 let m = m / d;
221
222 let m_n_diff = m as f64 - n as f64;
228 let mut risk = 0.9;
229 let threshold_1 = if 0.75 * m as f64 - 22.0 < 2250.0 {
230 0.75 * m as f64 - 22.0
231 } else {
232 2250.0
233 };
234 if m_n_diff >= threshold_1 {
235 return risk;
236 }
237 risk = 0.0001;
238 let threshold_2 = if 0.70 * m as f64 - 22.0 < 1500.0 {
239 0.70 * m as f64 - 22.0
240 } else {
241 1500.0
242 };
243 if m_n_diff >= threshold_2 {
244 return risk;
245 }
246 risk = 0.000001;
247 let threshold_3 = if 0.65 * m as f64 - 22.0 < 750.0 {
248 0.65 * m as f64
249 } else {
250 750.0
251 };
252 if m_n_diff >= threshold_3 {
253 return risk;
254 }
255 risk = 0.00000001;
256 risk
257 }
258
259 pub fn update_confirmation_risks(&self, g_inner: &ConsensusGraphInner) {
262 if g_inner.pivot_chain.len() > DEFERRED_STATE_EPOCH_COUNT as usize {
263 let w_0 = g_inner
264 .weight_tree
265 .get(g_inner.cur_era_genesis_block_arena_index);
266 let mut risks = VecDeque::new();
267 let mut epoch_num = g_inner
268 .pivot_index_to_height(g_inner.pivot_chain.len())
269 - DEFERRED_STATE_EPOCH_COUNT;
270 let mut count = 0;
271 while epoch_num > g_inner.cur_era_genesis_height
272 && count < CONFIRMATION_METER_MAX_NUM_MAINTAINED_RISK
273 {
274 let w_4 = self.inner.read().total_weight_in_past_2d.delta;
275 let risk = self.confirmation_risk(g_inner, w_0, w_4, epoch_num);
276 risks.push_front(risk);
277 epoch_num -= 1;
278 count += 1;
279 if risk <= CONFIRMATION_METER_MIN_MAINTAINED_RISK {
280 break;
281 }
282 }
283
284 if risks.is_empty() {
285 epoch_num = g_inner.cur_era_genesis_height;
286 } else {
287 epoch_num += 1;
288 }
289
290 let finality = &mut self.inner.write().finality_manager;
291 debug!("Confirmation Risk: {:?}", risks);
292 finality.lowest_epoch_num = epoch_num;
293 finality.risks_less_than = risks;
294 }
295 }
296
297 pub fn is_adaptive_possible(
302 &self, g_inner: &ConsensusGraphInner, me: usize,
303 ) -> bool {
304 let psi = CONFIRMATION_METER_PSI;
305 let mut cur_height = g_inner.cur_era_stable_height;
307 let mut cur_arena_index =
308 g_inner.get_pivot_block_arena_index(cur_height);
309 while g_inner.arena[cur_arena_index]
310 .data
311 .ledger_view_timer_chain_height
312 + CONFIRMATION_METER_ADAPTIVE_TEST_TIMER_DIFF
313 <= g_inner.arena[me].data.ledger_view_timer_chain_height
314 && cur_height < g_inner.best_epoch_number()
315 {
316 cur_height += 1;
317 cur_arena_index = g_inner.get_pivot_block_arena_index(cur_height);
318 }
319
320 if cur_height == g_inner.cur_era_stable_height {
321 return false;
322 }
323
324 let mut end_checking_height =
325 (cur_height - g_inner.cur_era_stable_height + psi - 1) / psi * psi
326 + g_inner.cur_era_stable_height;
327 if end_checking_height > g_inner.best_epoch_number() {
329 end_checking_height -= psi;
330 }
331 let n = (end_checking_height - g_inner.cur_era_stable_height) / psi;
332 let total_weight = g_inner
333 .weight_tree
334 .get(g_inner.cur_era_genesis_block_arena_index);
335 let me_index =
336 g_inner.height_to_pivot_index(g_inner.arena[me].data.epoch_number);
337 let x_3 =
338 total_weight - g_inner.pivot_chain_metadata[me_index].past_weight;
339
340 let mut adaptive_risk = 0f64;
341 let d = i128::try_from(g_inner.current_difficulty.low_u128()).unwrap();
342 for i in 0..n {
343 let a_pivot_index = g_inner.height_to_pivot_index(
344 g_inner.cur_era_stable_height + i * psi as u64,
345 );
346 let b_pivot_index = g_inner.height_to_pivot_index(
347 g_inner.cur_era_stable_height + (i + 1) * psi as u64,
348 );
349 let b = g_inner.pivot_chain[b_pivot_index];
350 let y = g_inner.weight_tree.get(b);
351 let mut x_1 = 0;
352 for v in a_pivot_index..b_pivot_index {
353 let pivot = g_inner.pivot_chain[v];
354 let next_pivot = g_inner.pivot_chain[v + 1];
355 for child in &g_inner.arena[pivot].children {
356 if *child != next_pivot {
357 let child_subtree_weight =
358 g_inner.weight_tree.get(*child);
359 x_1 = max(x_1, child_subtree_weight);
360 }
361 }
362 }
363 let n_j = (y
364 - x_1
365 - x_3
366 - self.inner.read().total_weight_in_past_2d.delta)
367 / d;
368 let m_j = (total_weight
369 - g_inner.pivot_chain_metadata[a_pivot_index].past_weight)
370 / d;
371
372 let i_risk =
373 10f64.powf((m_j as f64 / 3.0 - n_j as f64) / 700.0 + 5.3);
374 adaptive_risk += i_risk;
375 }
376
377 adaptive_risk > CONFIRMATION_METER_MAXIMUM_ADAPTIVE_RISK
378 }
379}