Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Commit 4eab867

Browse files
andresilvaAndronik Ordian
authored andcommitted
ethcore: fix pow difficulty validation (#9328)
* ethcore: fix pow difficulty validation * ethcore: validate difficulty is not zero * ethcore: add issue link to regression test * ethcore: fix tests * ethcore: move difficulty_to_boundary to ethash crate * ethcore: reuse difficulty_to_boundary and boundary_to_difficulty * ethcore: fix grumbles in difficulty_to_boundary_aux
1 parent 6e2821b commit 4eab867

File tree

8 files changed

+80
-55
lines changed

8 files changed

+80
-55
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ethash/Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ authors = ["Parity Technologies <admin@parity.io>"]
66
[lib]
77

88
[dependencies]
9-
log = "0.4"
10-
keccak-hash = { git = "https://github.com/paritytech/parity-common" }
11-
primal = "0.2.3"
12-
parking_lot = "0.6"
139
crunchy = "0.1.0"
14-
memmap = "0.6"
1510
either = "1.0.0"
11+
ethereum-types = "0.3"
12+
keccak-hash = { git = "https://github.com/paritytech/parity-common" }
13+
log = "0.4"
14+
memmap = "0.6"
15+
parking_lot = "0.6"
16+
primal = "0.2.3"
1617

1718
[dev-dependencies]
1819
tempdir = "0.3"

ethash/src/lib.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
#![cfg_attr(feature = "benches", feature(test))]
1818

19-
extern crate primal;
20-
extern crate parking_lot;
2119
extern crate either;
20+
extern crate ethereum_types;
2221
extern crate memmap;
22+
extern crate parking_lot;
23+
extern crate primal;
2324

2425
#[macro_use]
2526
extern crate crunchy;
@@ -38,6 +39,7 @@ mod shared;
3839
pub use cache::{NodeCacheBuilder, OptimizeFor};
3940
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
4041
use compute::Light;
42+
use ethereum_types::{U256, U512};
4143
use keccak::H256;
4244
use parking_lot::Mutex;
4345
pub use seed_compute::SeedHashCompute;
@@ -136,6 +138,29 @@ impl EthashManager {
136138
}
137139
}
138140

141+
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
142+
pub fn boundary_to_difficulty(boundary: &ethereum_types::H256) -> U256 {
143+
difficulty_to_boundary_aux(&**boundary)
144+
}
145+
146+
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
147+
pub fn difficulty_to_boundary(difficulty: &U256) -> ethereum_types::H256 {
148+
difficulty_to_boundary_aux(difficulty).into()
149+
}
150+
151+
fn difficulty_to_boundary_aux<T: Into<U512>>(difficulty: T) -> ethereum_types::U256 {
152+
let difficulty = difficulty.into();
153+
154+
assert!(!difficulty.is_zero());
155+
156+
if difficulty == U512::one() {
157+
U256::max_value()
158+
} else {
159+
// difficulty > 1, so result should never overflow 256 bits
160+
U256::from((U512::one() << 256) / difficulty)
161+
}
162+
}
163+
139164
#[test]
140165
fn test_lru() {
141166
use tempdir::TempDir;
@@ -155,6 +180,43 @@ fn test_lru() {
155180
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
156181
}
157182

183+
#[test]
184+
fn test_difficulty_to_boundary() {
185+
use ethereum_types::H256;
186+
use std::str::FromStr;
187+
188+
assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value()));
189+
assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap());
190+
assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap());
191+
assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap());
192+
}
193+
194+
#[test]
195+
fn test_difficulty_to_boundary_regression() {
196+
use ethereum_types::H256;
197+
198+
// the last bit was originally being truncated when performing the conversion
199+
// https://github.com/paritytech/parity-ethereum/issues/8397
200+
for difficulty in 1..9 {
201+
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into())));
202+
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into())));
203+
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into()));
204+
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into()));
205+
}
206+
}
207+
208+
#[test]
209+
#[should_panic]
210+
fn test_difficulty_to_boundary_panics_on_zero() {
211+
difficulty_to_boundary(&U256::from(0));
212+
}
213+
214+
#[test]
215+
#[should_panic]
216+
fn test_boundary_to_difficulty_panics_on_zero() {
217+
boundary_to_difficulty(&ethereum_types::H256::from(0));
218+
}
219+
158220
#[cfg(feature = "benches")]
159221
mod benchmarks {
160222
extern crate test;

ethcore/src/ethereum/ethash.rs

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::collections::BTreeMap;
2020
use std::sync::Arc;
2121
use hash::{KECCAK_EMPTY_LIST_RLP};
2222
use engines::block_reward::{self, RewardKind};
23-
use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
23+
use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
2424
use ethereum_types::{H256, H64, U256, Address};
2525
use unexpected::{OutOfBounds, Mismatch};
2626
use block::*;
@@ -302,7 +302,7 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
302302
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() })))
303303
}
304304

305-
let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
305+
let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
306306
&header.bare_hash().0,
307307
seal.nonce.low_u64(),
308308
&seal.mix_hash.0
@@ -324,7 +324,7 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
324324

325325
let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.low_u64());
326326
let mix = H256(result.mix_hash);
327-
let difficulty = Ethash::boundary_to_difficulty(&H256(result.value));
327+
let difficulty = ethash::boundary_to_difficulty(&H256(result.value));
328328
trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}",
329329
num = header.number() as u64,
330330
seed = H256(slow_hash_block_number(header.number() as u64)),
@@ -447,25 +447,6 @@ impl Ethash {
447447
}
448448
target
449449
}
450-
451-
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
452-
pub fn boundary_to_difficulty(boundary: &H256) -> U256 {
453-
let d = U256::from(*boundary);
454-
if d <= U256::one() {
455-
U256::max_value()
456-
} else {
457-
((U256::one() << 255) / d) << 1
458-
}
459-
}
460-
461-
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
462-
pub fn difficulty_to_boundary(difficulty: &U256) -> H256 {
463-
if *difficulty <= U256::one() {
464-
U256::max_value().into()
465-
} else {
466-
(((U256::one() << 255) / *difficulty) << 1).into()
467-
}
468-
}
469450
}
470451

471452
fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) {
@@ -766,16 +747,6 @@ mod tests {
766747
}
767748
}
768749

769-
#[test]
770-
fn test_difficulty_to_boundary() {
771-
// result of f(0) is undefined, so do not assert the result
772-
let _ = Ethash::difficulty_to_boundary(&U256::from(0));
773-
assert_eq!(Ethash::difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value()));
774-
assert_eq!(Ethash::difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap());
775-
assert_eq!(Ethash::difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap());
776-
assert_eq!(Ethash::difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap());
777-
}
778-
779750
#[test]
780751
fn difficulty_frontier() {
781752
let machine = new_homestead_test_machine();

ethcore/src/miner/stratum.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ use std::fmt;
2222

2323
use client::{Client, ImportSealedBlock};
2424
use ethereum_types::{H64, H256, clean_0x, U256};
25-
use ethereum::ethash::Ethash;
26-
use ethash::SeedHashCompute;
25+
use ethash::{self, SeedHashCompute};
2726
#[cfg(feature = "work-notify")]
2827
use ethcore_miner::work_notify::NotifyWork;
2928
#[cfg(feature = "work-notify")]
@@ -167,7 +166,7 @@ impl StratumJobDispatcher {
167166
/// Serializes payload for stratum service
168167
fn payload(&self, pow_hash: H256, difficulty: U256, number: u64) -> String {
169168
// TODO: move this to engine
170-
let target = Ethash::difficulty_to_boundary(&difficulty);
169+
let target = ethash::difficulty_to_boundary(&difficulty);
171170
let seed_hash = &self.seed_compute.lock().hash_block_number(number);
172171
let seed_hash = H256::from_slice(&seed_hash[..]);
173172
format!(

json/src/spec/ethash.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use hash::Address;
2424
pub struct EthashParams {
2525
/// See main EthashParams docs.
2626
#[serde(rename="minimumDifficulty")]
27+
#[serde(deserialize_with="uint::validate_non_zero")]
2728
pub minimum_difficulty: Uint,
2829
/// See main EthashParams docs.
2930
#[serde(rename="difficultyBoundDivisor")]

miner/src/work_notify.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,10 @@ impl WorkPoster {
6767
}
6868
}
6969

70-
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
71-
fn difficulty_to_boundary(difficulty: &U256) -> H256 {
72-
if *difficulty <= U256::one() {
73-
U256::max_value().into()
74-
} else {
75-
(((U256::one() << 255) / *difficulty) << 1).into()
76-
}
77-
}
78-
7970
impl NotifyWork for WorkPoster {
8071
fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
8172
// TODO: move this to engine
82-
let target = difficulty_to_boundary(&difficulty);
73+
let target = ethash::difficulty_to_boundary(&difficulty);
8374
let seed_hash = &self.seed_compute.lock().hash_block_number(number);
8475
let seed_hash = H256::from_slice(&seed_hash[..]);
8576
let body = format!(

rpc/src/v1/impls/eth.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ use rlp::{self, Rlp};
2424
use ethereum_types::{U256, H64, H256, Address};
2525
use parking_lot::Mutex;
2626

27-
use ethash::SeedHashCompute;
27+
use ethash::{self, SeedHashCompute};
2828
use ethcore::account_provider::AccountProvider;
2929
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
30-
use ethcore::ethereum::Ethash;
3130
use ethcore::filter::Filter as EthcoreFilter;
3231
use ethcore::header::{BlockNumber as EthBlockNumber};
3332
use ethcore::log_entry::LogEntry;
@@ -758,7 +757,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
758757
})?;
759758

760759
let (pow_hash, number, timestamp, difficulty) = work;
761-
let target = Ethash::difficulty_to_boundary(&difficulty);
760+
let target = ethash::difficulty_to_boundary(&difficulty);
762761
let seed_hash = self.seed_compute.lock().hash_block_number(number);
763762

764763
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();

0 commit comments

Comments
 (0)