cfxcore_pow/
target_difficulty_manager.rs1use crate::{ConsensusProvider, ProofOfWorkConfig};
2use cfx_types::{H256, U256};
3use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
4use malloc_size_of_derive::MallocSizeOf as DeriveMallocSizeOf;
5use parking_lot::RwLock;
6use std::collections::{HashMap, VecDeque};
7
8#[derive(DeriveMallocSizeOf)]
10struct TargetDifficultyCacheInner {
11 capacity: usize,
12 meta: VecDeque<H256>,
13 cache: HashMap<H256, U256>,
14}
15
16impl TargetDifficultyCacheInner {
17 pub fn new(capacity: usize) -> Self {
18 TargetDifficultyCacheInner {
19 capacity,
20 meta: Default::default(),
21 cache: Default::default(),
22 }
23 }
24
25 pub fn is_full(&self) -> bool { self.meta.len() >= self.capacity }
26
27 pub fn evict_one(&mut self) {
28 let hash = self.meta.pop_front();
29 if let Some(h) = hash {
30 self.cache.remove(&h);
31 }
32 }
33
34 pub fn insert(&mut self, hash: H256, difficulty: U256) {
35 self.meta.push_back(hash.clone());
36 self.cache.insert(hash, difficulty);
37 }
38}
39
40struct TargetDifficultyCache {
41 inner: RwLock<TargetDifficultyCacheInner>,
42}
43
44impl MallocSizeOf for TargetDifficultyCache {
45 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
46 self.inner.read().size_of(ops)
47 }
48}
49
50impl TargetDifficultyCache {
51 pub fn new(capacity: usize) -> Self {
52 TargetDifficultyCache {
53 inner: RwLock::new(TargetDifficultyCacheInner::new(capacity)),
54 }
55 }
56
57 pub fn get(&self, hash: &H256) -> Option<U256> {
58 let inner = self.inner.read();
59 inner.cache.get(hash).map(|diff| *diff)
60 }
61
62 pub fn set(&self, hash: H256, difficulty: U256) {
63 let mut inner = self.inner.write();
64 while inner.is_full() {
65 inner.evict_one();
66 }
67 inner.insert(hash, difficulty);
68 }
69}
70
71#[derive(DeriveMallocSizeOf)]
76pub struct TargetDifficultyManager {
77 cache: TargetDifficultyCache,
78}
79
80impl TargetDifficultyManager {
81 pub fn new(capacity: usize) -> Self {
82 TargetDifficultyManager {
83 cache: TargetDifficultyCache::new(capacity),
84 }
85 }
86
87 pub fn get(&self, hash: &H256) -> Option<U256> { self.cache.get(hash) }
88
89 pub fn set(&self, hash: H256, difficulty: U256) {
90 self.cache.set(hash, difficulty);
91 }
92
93 pub fn target_difficulty<C>(
100 &self, consensus: C, pow_config: &ProofOfWorkConfig, cur_hash: &H256,
101 ) -> U256
102 where C: ConsensusProvider {
103 if let Some(target_diff) = self.get(cur_hash) {
104 return target_diff;
107 }
108
109 let mut cur_header = consensus
110 .block_header_by_hash(cur_hash)
111 .expect("Must already in BlockDataManager block_header");
112 let epoch = cur_header.height();
113 assert_ne!(epoch, 0);
114 debug_assert!(
115 epoch
116 == (epoch
117 / pow_config.difficulty_adjustment_epoch_period(epoch))
118 * pow_config.difficulty_adjustment_epoch_period(epoch)
119 );
120
121 let mut cur = cur_hash.clone();
122 let cur_difficulty = cur_header.difficulty().clone();
123 let mut block_count = 0 as u64;
124 let max_time = cur_header.timestamp();
125 let mut min_time = 0;
126
127 for _ in 0..pow_config.difficulty_adjustment_epoch_period(epoch) {
129 block_count += consensus.num_blocks_in_epoch(&cur);
130 cur = cur_header.parent_hash().clone();
131 cur_header = consensus.block_header_by_hash(&cur).unwrap();
132 if cur_header.timestamp() != 0 {
133 min_time = cur_header.timestamp();
134 }
135 assert!(max_time >= min_time);
136 }
137
138 let expected_diff = pow_config.target_difficulty(
139 block_count,
140 max_time - min_time,
141 &cur_difficulty,
142 );
143 let mut target_diff = if epoch < pow_config.cip86_height {
147 expected_diff
148 } else {
149 cur_difficulty / 5 * 4 + expected_diff / 5
150 };
151
152 let (lower, upper) = pow_config.get_adjustment_bound(cur_difficulty);
153 if target_diff > upper {
154 target_diff = upper;
155 }
156 if target_diff < lower {
157 target_diff = lower;
158 }
159
160 self.set(*cur_hash, target_diff);
162
163 target_diff
164 }
165}