1mod cache;
6mod compute;
7mod keccak;
8mod seed_compute;
9mod shared;
10mod target_difficulty_manager;
11mod traits;
12
13pub use traits::ConsensusProvider;
14
15pub use self::{
16 cache::CacheBuilder, shared::POW_STAGE_LENGTH,
17 target_difficulty_manager::TargetDifficultyManager,
18};
19use keccak_hash::keccak as keccak_hash;
20
21use cfx_parameters::pow::*;
22use cfx_types::{BigEndianHash, H256, U256, U512};
23
24use malloc_size_of_derive::MallocSizeOf as DeriveMallocSizeOf;
25use primitives::BlockHeader;
26use static_assertions::_core::str::FromStr;
27use std::convert::TryFrom;
28
29#[cfg(target_endian = "big")]
30compile_error!("The PoW implementation requires little-endian platform");
31
32#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
33pub struct ProofOfWorkProblem {
34 pub block_height: u64,
35 pub block_hash: H256,
36 pub difficulty: U256,
37 pub boundary: U256,
38}
39
40impl ProofOfWorkProblem {
41 pub const NO_BOUNDARY: U256 = U256::MAX;
42
43 pub fn new(block_height: u64, block_hash: H256, difficulty: U256) -> Self {
44 let boundary = difficulty_to_boundary(&difficulty);
45 Self {
46 block_height,
47 block_hash,
48 difficulty,
49 boundary,
50 }
51 }
52
53 pub fn from_block_header(block_header: &BlockHeader) -> Self {
54 Self::new(
55 block_header.height(),
56 block_header.problem_hash(),
57 *block_header.difficulty(),
58 )
59 }
60
61 #[inline]
62 pub fn validate_hash_against_boundary(
63 hash: &H256, nonce: &U256, boundary: &U256,
64 ) -> bool {
65 let lower_bound = nonce_to_lower_bound(nonce);
66 let (against_lower_bound_u256, _) =
67 BigEndianHash::into_uint(hash).overflowing_sub(lower_bound);
68 against_lower_bound_u256.lt(boundary)
69 || boundary.eq(&ProofOfWorkProblem::NO_BOUNDARY)
70 }
71}
72
73#[derive(Debug, Copy, Clone)]
74pub struct ProofOfWorkSolution {
75 pub nonce: U256,
76}
77
78#[derive(Debug, Clone, DeriveMallocSizeOf)]
79pub enum MiningType {
80 Stratum,
81 CPU,
82 Disable,
83}
84
85impl FromStr for MiningType {
86 type Err = String;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 let mining_type = match s {
90 "stratum" => Self::Stratum,
91 "cpu" => Self::CPU,
92 "disable" => Self::Disable,
93 _ => return Err("invalid mining type".into()),
94 };
95 Ok(mining_type)
96 }
97}
98
99#[derive(Debug, Clone, DeriveMallocSizeOf)]
100pub struct ProofOfWorkConfig {
101 pub test_mode: bool,
102 pub use_octopus_in_test_mode: bool,
103 pub mining_type: MiningType,
104 pub initial_difficulty: u64,
105 pub block_generation_period: u64,
106 pub stratum_listen_addr: String,
107 pub stratum_port: u16,
108 pub stratum_secret: Option<H256>,
109 pub pow_problem_window_size: usize,
110 pub cip86_height: u64,
111}
112
113impl ProofOfWorkConfig {
114 pub fn new(
115 test_mode: bool, use_octopus_in_test_mode: bool, mining_type: &str,
116 initial_difficulty: Option<u64>, stratum_listen_addr: String,
117 stratum_port: u16, stratum_secret: Option<H256>,
118 pow_problem_window_size: usize, cip86_height: u64,
119 ) -> Self {
120 if test_mode {
121 ProofOfWorkConfig {
122 test_mode,
123 use_octopus_in_test_mode,
124 mining_type: mining_type.parse().expect("Invalid mining type"),
125 initial_difficulty: initial_difficulty.unwrap_or(4),
126 block_generation_period: 1000000,
127 stratum_listen_addr,
128 stratum_port,
129 stratum_secret,
130 pow_problem_window_size,
131 cip86_height,
132 }
133 } else {
134 ProofOfWorkConfig {
135 test_mode,
136 use_octopus_in_test_mode,
137 mining_type: mining_type.parse().expect("Invalid mining type"),
138 initial_difficulty: INITIAL_DIFFICULTY,
139 block_generation_period: TARGET_AVERAGE_BLOCK_GENERATION_PERIOD,
140 stratum_listen_addr,
141 stratum_port,
142 stratum_secret,
143 pow_problem_window_size,
144 cip86_height,
145 }
146 }
147 }
148
149 pub fn difficulty_adjustment_epoch_period(&self, cur_height: u64) -> u64 {
150 if self.test_mode {
151 20
152 } else {
153 if cur_height > self.cip86_height {
154 DIFFICULTY_ADJUSTMENT_EPOCH_PERIOD_CIP
155 } else {
156 DIFFICULTY_ADJUSTMENT_EPOCH_PERIOD
157 }
158 }
159 }
160
161 pub fn use_octopus(&self) -> bool {
162 !self.test_mode || self.use_octopus_in_test_mode
163 }
164
165 pub fn use_stratum(&self) -> bool {
166 matches!(self.mining_type, MiningType::Stratum)
167 }
168
169 pub fn enable_mining(&self) -> bool {
170 !matches!(self.mining_type, MiningType::Disable)
171 }
172
173 pub fn target_difficulty(
174 &self, block_count: u64, timespan: u64, cur_difficulty: &U256,
175 ) -> U256 {
176 if timespan == 0 || block_count <= 1 || self.test_mode {
177 return self.initial_difficulty.into();
178 }
179
180 let target = (U512::from(*cur_difficulty)
181 * U512::from(self.block_generation_period)
182 * U512::from(block_count - 1))
184 / (U512::from(timespan) * U512::from(1000000));
185 if target.is_zero() {
186 return 1.into();
187 }
188 if target > U256::max_value().into() {
189 return U256::max_value();
190 }
191 U256::try_from(target).unwrap()
192 }
193
194 pub fn get_adjustment_bound(&self, diff: U256) -> (U256, U256) {
195 let adjustment = diff / DIFFICULTY_ADJUSTMENT_FACTOR;
196 let mut min_diff = diff - adjustment;
197 let mut max_diff = diff + adjustment;
198 let initial_diff: U256 = self.initial_difficulty.into();
199
200 if min_diff < initial_diff {
201 min_diff = initial_diff;
202 }
203
204 if max_diff < min_diff {
205 max_diff = min_diff;
206 }
207
208 (min_diff, max_diff)
209 }
210}
211
212pub fn nonce_to_lower_bound(nonce: &U256) -> U256 {
216 let mut buf = [0u8; 32];
217 nonce.to_big_endian(&mut buf[..]);
218 for i in 16..32 {
219 buf[i] = 0;
220 }
221 buf[0] = buf[0] & 0x7f;
222 let lower_bound = U256::from(buf);
224 lower_bound
225}
226
227pub fn pow_hash_to_quality(hash: &H256, nonce: &U256) -> U256 {
228 let hash_as_uint = BigEndianHash::into_uint(hash);
229 let lower_bound = nonce_to_lower_bound(nonce);
230 let (against_bound_u256, _) = hash_as_uint.overflowing_sub(lower_bound);
231 if against_bound_u256.eq(&U256::MAX) {
232 U256::one()
233 } else {
234 boundary_to_difficulty(&(against_bound_u256 + U256::one()))
235 }
236}
237
238pub fn pow_quality_to_hash(pow_quality: &U256, nonce: &U256) -> H256 {
240 let lower_bound = nonce_to_lower_bound(nonce);
241 let hash_u256 = if pow_quality.eq(&U256::MAX) {
242 U256::one()
243 } else {
244 let boundary = difficulty_to_boundary(&(pow_quality + U256::one()));
245 let (against_bound_u256, _) = boundary.overflowing_add(lower_bound);
246 against_bound_u256
247 };
248 BigEndianHash::from_uint(&hash_u256)
249}
250
251pub fn boundary_to_difficulty(boundary: &U256) -> U256 {
254 assert!(!boundary.is_zero());
255 if boundary.eq(&U256::one()) {
256 U256::MAX
257 } else {
258 compute_inv_x_times_2_pow_256_floor(boundary)
259 }
260}
261
262pub fn difficulty_to_boundary(difficulty: &U256) -> U256 {
265 assert!(!difficulty.is_zero());
266 if difficulty.eq(&U256::one()) {
267 ProofOfWorkProblem::NO_BOUNDARY
268 } else {
269 compute_inv_x_times_2_pow_256_floor(difficulty)
270 }
271}
272
273pub fn compute_inv_x_times_2_pow_256_floor(x: &U256) -> U256 {
275 let (div, modular) = U256::MAX.clone().div_mod(x.clone());
276 if &(modular + U256::one()) == x {
277 div + U256::one()
278 } else {
279 div
280 }
281}
282
283pub struct PowComputer {
284 use_octopus: bool,
285 cache_builder: CacheBuilder,
286}
287
288impl PowComputer {
289 pub fn new(use_octopus: bool) -> Self {
290 PowComputer {
291 use_octopus,
292 cache_builder: CacheBuilder::new(),
293 }
294 }
295
296 pub fn compute(
297 &self, nonce: &U256, block_hash: &H256, block_height: u64,
298 ) -> H256 {
299 if !self.use_octopus {
300 let mut buf = [0u8; 64];
301 for i in 0..32 {
302 buf[i] = block_hash[i];
303 }
304 nonce.to_little_endian(&mut buf[32..64]);
305 let intermediate = keccak_hash(&buf[..]);
306 let mut tmp = [0u8; 32];
307 for i in 0..32 {
308 tmp[i] = intermediate[i] ^ block_hash[i]
309 }
310 keccak_hash(tmp)
311 } else {
312 let light = self.cache_builder.light(block_height);
313 light
314 .compute(block_hash.as_fixed_bytes(), nonce.low_u64())
315 .into()
316 }
317 }
318
319 pub fn validate(
320 &self, problem: &ProofOfWorkProblem, solution: &ProofOfWorkSolution,
321 ) -> bool {
322 let nonce = solution.nonce;
323 let hash =
324 self.compute(&nonce, &problem.block_hash, problem.block_height);
325 ProofOfWorkProblem::validate_hash_against_boundary(
326 &hash,
327 &nonce,
328 &problem.boundary,
329 )
330 }
331}
332
333#[test]
334fn test_octopus() {
335 let pow = PowComputer::new(true);
336
337 let block_hash =
338 "4d99d0b41c7eb0dd1a801c35aae2df28ae6b53bc7743f0818a34b6ec97f5b4ae"
339 .parse()
340 .unwrap();
341 let start_nonce = 0x2333333333u64 & (!0x1f);
342 pow.compute(&U256::from(start_nonce), &block_hash, 2);
343}