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_from_m_n(m: i128, n: i128) -> f64 {
181 let m_n_diff = m as f64 - n as f64;
182 let mut risk = 0.9;
183 let threshold_1 = if 0.75 * m as f64 - 22.0 < 2250.0 {
184 0.75 * m as f64 - 22.0
185 } else {
186 2250.0
187 };
188 if m_n_diff >= threshold_1 {
189 return risk;
190 }
191 risk = 0.0001;
192 let threshold_2 = if 0.70 * m as f64 - 22.0 < 1500.0 {
193 0.70 * m as f64 - 22.0
194 } else {
195 1500.0
196 };
197 if m_n_diff >= threshold_2 {
198 return risk;
199 }
200 risk = 0.000001;
201 let threshold_3 = if 0.65 * m as f64 - 22.0 < 750.0 {
202 0.65 * m as f64 - 22.0
203 } else {
204 750.0
205 };
206 if m_n_diff >= threshold_3 {
207 return risk;
208 }
209 risk = 0.00000001;
210 risk
211 }
212
213 fn confirmation_risk(
214 &self, g_inner: &ConsensusGraphInner, w_0: i128, w_4: i128,
215 epoch_num: u64,
216 ) -> f64 {
217 let idx = g_inner.get_pivot_block_arena_index(epoch_num);
219 let pivot_idx = g_inner.height_to_pivot_index(epoch_num);
220 let w_1 = g_inner.weight_tree.get(idx);
221
222 let parent = g_inner.arena[idx].parent;
224 assert!(parent != NULL);
225 let mut max_weight = 0;
226 for child in g_inner.arena[parent].children.iter() {
227 if *child == idx {
228 continue;
229 }
230
231 let child_weight = g_inner.weight_tree.get(*child);
232 if child_weight > max_weight {
233 max_weight = child_weight;
234 }
235 }
236 let w_2 = max_weight;
237
238 let w_3 = g_inner.pivot_chain_metadata[pivot_idx].past_weight;
240
241 let d = i128::try_from(g_inner.current_difficulty.low_u128()).unwrap();
243
244 let w_2_4 = w_2 + w_4;
246 let n = if w_1 >= w_2_4 { w_1 - w_2_4 } else { 0 };
247
248 let n = (n / d) + 1;
249
250 let m = if w_0 >= w_3 { w_0 - w_3 } else { 0 };
252
253 let m = m / d;
254
255 Self::confirmation_risk_from_m_n(m, n)
260 }
261
262 pub fn update_confirmation_risks(&self, g_inner: &ConsensusGraphInner) {
265 if g_inner.pivot_chain.len() > DEFERRED_STATE_EPOCH_COUNT as usize {
266 let w_0 = g_inner
267 .weight_tree
268 .get(g_inner.cur_era_genesis_block_arena_index);
269 let mut risks = VecDeque::new();
270 let mut epoch_num = g_inner
271 .pivot_index_to_height(g_inner.pivot_chain.len())
272 - DEFERRED_STATE_EPOCH_COUNT;
273 let mut count = 0;
274 while epoch_num > g_inner.cur_era_genesis_height
275 && count < CONFIRMATION_METER_MAX_NUM_MAINTAINED_RISK
276 {
277 let w_4 = self.inner.read().total_weight_in_past_2d.delta;
278 let risk = self.confirmation_risk(g_inner, w_0, w_4, epoch_num);
279 risks.push_front(risk);
280 epoch_num -= 1;
281 count += 1;
282 if risk <= CONFIRMATION_METER_MIN_MAINTAINED_RISK {
283 break;
284 }
285 }
286
287 if risks.is_empty() {
288 epoch_num = g_inner.cur_era_genesis_height;
289 } else {
290 epoch_num += 1;
291 }
292
293 let finality = &mut self.inner.write().finality_manager;
294 debug!("Confirmation Risk: {:?}", risks);
295 finality.lowest_epoch_num = epoch_num;
296 finality.risks_less_than = risks;
297 }
298 }
299
300 pub fn is_adaptive_possible(
305 &self, g_inner: &ConsensusGraphInner, me: usize,
306 ) -> bool {
307 let psi = CONFIRMATION_METER_PSI;
308 let mut cur_height = g_inner.cur_era_stable_height;
310 let mut cur_arena_index =
311 g_inner.get_pivot_block_arena_index(cur_height);
312 while g_inner.arena[cur_arena_index]
313 .data
314 .ledger_view_timer_chain_height
315 + CONFIRMATION_METER_ADAPTIVE_TEST_TIMER_DIFF
316 <= g_inner.arena[me].data.ledger_view_timer_chain_height
317 && cur_height < g_inner.best_epoch_number()
318 {
319 cur_height += 1;
320 cur_arena_index = g_inner.get_pivot_block_arena_index(cur_height);
321 }
322
323 if cur_height == g_inner.cur_era_stable_height {
324 return false;
325 }
326
327 let mut end_checking_height =
328 (cur_height - g_inner.cur_era_stable_height + psi - 1) / psi * psi
329 + g_inner.cur_era_stable_height;
330 if end_checking_height > g_inner.best_epoch_number() {
332 end_checking_height -= psi;
333 }
334 let n = (end_checking_height - g_inner.cur_era_stable_height) / psi;
335 let total_weight = g_inner
336 .weight_tree
337 .get(g_inner.cur_era_genesis_block_arena_index);
338 let me_index =
339 g_inner.height_to_pivot_index(g_inner.arena[me].data.epoch_number);
340 let x_3 =
341 total_weight - g_inner.pivot_chain_metadata[me_index].past_weight;
342
343 let mut adaptive_risk = 0f64;
344 let d = i128::try_from(g_inner.current_difficulty.low_u128()).unwrap();
345 for i in 0..n {
346 let a_pivot_index = g_inner.height_to_pivot_index(
347 g_inner.cur_era_stable_height + i * psi as u64,
348 );
349 let b_pivot_index = g_inner.height_to_pivot_index(
350 g_inner.cur_era_stable_height + (i + 1) * psi as u64,
351 );
352 let b = g_inner.pivot_chain[b_pivot_index];
353 let y = g_inner.weight_tree.get(b);
354 let mut x_1 = 0;
355 for v in a_pivot_index..b_pivot_index {
356 let pivot = g_inner.pivot_chain[v];
357 let next_pivot = g_inner.pivot_chain[v + 1];
358 for child in &g_inner.arena[pivot].children {
359 if *child != next_pivot {
360 let child_subtree_weight =
361 g_inner.weight_tree.get(*child);
362 x_1 = max(x_1, child_subtree_weight);
363 }
364 }
365 }
366 let n_j = (y
367 - x_1
368 - x_3
369 - self.inner.read().total_weight_in_past_2d.delta)
370 / d;
371 let m_j = (total_weight
372 - g_inner.pivot_chain_metadata[a_pivot_index].past_weight)
373 / d;
374
375 let i_risk =
376 10f64.powf((m_j as f64 / 3.0 - n_j as f64) / 700.0 + 5.3);
377 adaptive_risk += i_risk;
378 }
379
380 adaptive_risk > CONFIRMATION_METER_MAXIMUM_ADAPTIVE_RISK
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::ConfirmationMeter;
387
388 #[test]
389 fn confirmation_risk_uses_offset_in_lowest_threshold() {
390 assert_eq!(
391 ConfirmationMeter::confirmation_risk_from_m_n(500, 190),
392 0.000001,
393 );
394 assert_eq!(
395 ConfirmationMeter::confirmation_risk_from_m_n(500, 198),
396 0.00000001,
397 );
398 }
399}