cfxcore_pow/
target_difficulty_manager.rsuse crate::{ConsensusProvider, ProofOfWorkConfig};
use cfx_types::{H256, U256};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf as DeriveMallocSizeOf;
use parking_lot::RwLock;
use std::collections::{HashMap, VecDeque};
#[derive(DeriveMallocSizeOf)]
struct TargetDifficultyCacheInner {
capacity: usize,
meta: VecDeque<H256>,
cache: HashMap<H256, U256>,
}
impl TargetDifficultyCacheInner {
pub fn new(capacity: usize) -> Self {
TargetDifficultyCacheInner {
capacity,
meta: Default::default(),
cache: Default::default(),
}
}
pub fn is_full(&self) -> bool { self.meta.len() >= self.capacity }
pub fn evict_one(&mut self) {
let hash = self.meta.pop_front();
if let Some(h) = hash {
self.cache.remove(&h);
}
}
pub fn insert(&mut self, hash: H256, difficulty: U256) {
self.meta.push_back(hash.clone());
self.cache.insert(hash, difficulty);
}
}
struct TargetDifficultyCache {
inner: RwLock<TargetDifficultyCacheInner>,
}
impl MallocSizeOf for TargetDifficultyCache {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.inner.read().size_of(ops)
}
}
impl TargetDifficultyCache {
pub fn new(capacity: usize) -> Self {
TargetDifficultyCache {
inner: RwLock::new(TargetDifficultyCacheInner::new(capacity)),
}
}
pub fn get(&self, hash: &H256) -> Option<U256> {
let inner = self.inner.read();
inner.cache.get(hash).map(|diff| *diff)
}
pub fn set(&self, hash: H256, difficulty: U256) {
let mut inner = self.inner.write();
while inner.is_full() {
inner.evict_one();
}
inner.insert(hash, difficulty);
}
}
#[derive(DeriveMallocSizeOf)]
pub struct TargetDifficultyManager {
cache: TargetDifficultyCache,
}
impl TargetDifficultyManager {
pub fn new(capacity: usize) -> Self {
TargetDifficultyManager {
cache: TargetDifficultyCache::new(capacity),
}
}
pub fn get(&self, hash: &H256) -> Option<U256> { self.cache.get(hash) }
pub fn set(&self, hash: H256, difficulty: U256) {
self.cache.set(hash, difficulty);
}
pub fn target_difficulty<C>(
&self, consensus: C, pow_config: &ProofOfWorkConfig, cur_hash: &H256,
) -> U256
where C: ConsensusProvider {
if let Some(target_diff) = self.get(cur_hash) {
return target_diff;
}
let mut cur_header = consensus
.block_header_by_hash(cur_hash)
.expect("Must already in BlockDataManager block_header");
let epoch = cur_header.height();
assert_ne!(epoch, 0);
debug_assert!(
epoch
== (epoch
/ pow_config.difficulty_adjustment_epoch_period(epoch))
* pow_config.difficulty_adjustment_epoch_period(epoch)
);
let mut cur = cur_hash.clone();
let cur_difficulty = cur_header.difficulty().clone();
let mut block_count = 0 as u64;
let max_time = cur_header.timestamp();
let mut min_time = 0;
for _ in 0..pow_config.difficulty_adjustment_epoch_period(epoch) {
block_count += consensus.num_blocks_in_epoch(&cur);
cur = cur_header.parent_hash().clone();
cur_header = consensus.block_header_by_hash(&cur).unwrap();
if cur_header.timestamp() != 0 {
min_time = cur_header.timestamp();
}
assert!(max_time >= min_time);
}
let expected_diff = pow_config.target_difficulty(
block_count,
max_time - min_time,
&cur_difficulty,
);
let mut target_diff = if epoch < pow_config.cip86_height {
expected_diff
} else {
cur_difficulty / 5 * 4 + expected_diff / 5
};
let (lower, upper) = pow_config.get_adjustment_bound(cur_difficulty);
if target_diff > upper {
target_diff = upper;
}
if target_diff < lower {
target_diff = lower;
}
self.set(*cur_hash, target_diff);
target_diff
}
}