Skip to content

Commit 58e8fa4

Browse files
committed
Add tests and fix hashimoto
1 parent 6d3ee77 commit 58e8fa4

File tree

3 files changed

+126
-26
lines changed

3 files changed

+126
-26
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ license = "Apache-2.0"
88
[dependencies]
99
etcommon-bigint = "0.2"
1010
etcommon-rlp = "0.2"
11+
byteorder = "1.0"
1112
sha3 = "0.6"
13+
14+
[dev-dependencies]
15+
etcommon-block = "0.2"
16+
etcommon-hexutil = "0.2"
17+
blockchain = "0.1"

src/lib.rs

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod miller_rabin;
1212
use miller_rabin::is_prime;
1313
use sha3::{Digest, Keccak256, Keccak512};
1414
use bigint::{H1024, U256, H256, H64, H512};
15+
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
1516
use rlp::Encodable;
1617
use std::ops::BitXor;
1718

@@ -118,13 +119,27 @@ fn fnv64(a: [u8; 64], b: [u8; 64]) -> [u8; 64] {
118119
r
119120
}
120121

122+
fn fnv128(a: [u8; 128], b: [u8; 128]) -> [u8; 128] {
123+
let mut r = [0u8; 128];
124+
for i in 0..(128 / 4) {
125+
let j = i * 4;
126+
let a32 = (&a[j..]).read_u32::<LittleEndian>().unwrap();
127+
let b32 = (&b[j..]).read_u32::<LittleEndian>().unwrap();
128+
129+
(&mut r[j..]).write_u32::<LittleEndian>(
130+
fnv((&a[j..]).read_u32::<LittleEndian>().unwrap(),
131+
(&b[j..]).read_u32::<LittleEndian>().unwrap()));
132+
}
133+
r
134+
}
135+
121136
fn u8s_to_u32(a: &[u8]) -> u32 {
122137
let n = a.len();
123138
(a[0] as u32) + (a[1] as u32) << 8 +
124139
(a[2] as u32) << 16 + (a[3] as u32) << 24
125140
}
126141

127-
fn calc_dataset_item(cache: &[u8], i: usize) -> H512 {
142+
pub fn calc_dataset_item(cache: &[u8], i: usize) -> H512 {
128143
debug_assert!(cache.len() % 64 == 0);
129144

130145
let n = cache.len() / 64;
@@ -171,45 +186,48 @@ pub fn make_dataset(dataset: &mut [u8], cache: &[u8]) {
171186
/// "Main" function of Ethash, calculating the mix digest and result given the
172187
/// header and nonce.
173188
pub fn hashimoto<F: Fn(usize) -> H512>(
174-
header: &[u8], nonce: H64, full_size: usize, lookup: F
175-
) -> (H1024, H256) {
189+
header_hash: H256, nonce: H64, full_size: usize, lookup: F
190+
) -> (H256, H256) {
176191
let n = full_size / HASH_BYTES;
177192
let w = MIX_BYTES / WORD_BYTES;
178193
const MIXHASHES: usize = MIX_BYTES / HASH_BYTES;
179194
let s = {
180195
let mut hasher = Keccak512::default();
181-
hasher.input(header);
182-
hasher.input(&nonce);
196+
let mut reversed_nonce: Vec<u8> = nonce.as_ref().into();
197+
reversed_nonce.reverse();
198+
hasher.input(&header_hash);
199+
hasher.input(&reversed_nonce);
183200
hasher.result()
184201
};
185202
let mut mix = [0u8; MIX_BYTES];
186203
for i in 0..MIXHASHES {
187204
for j in 0..64 {
188-
mix[i * MIXHASHES] = s[j];
205+
mix[i * HASH_BYTES + j] = s[j];
189206
}
190207
}
208+
191209
for i in 0..ACCESSES {
192-
let p = (fnv(i.bitxor(s[0] as usize) as u32,
193-
mix[i % w] as u32) as usize) % (n / MIXHASHES) * MIXHASHES;
194-
let newdata = [0u8; MIX_BYTES];
210+
let p = (fnv((i as u32).bitxor(s.as_ref().read_u32::<LittleEndian>().unwrap()),
211+
(&mix[(i % w * 4)..]).read_u32::<LittleEndian>().unwrap())
212+
as usize) % (n / MIXHASHES) * MIXHASHES;
213+
let mut newdata = [0u8; MIX_BYTES];
195214
for j in 0..MIXHASHES {
196215
let v = lookup(p + j);
197-
let mut newdata = [0u8; MIXHASHES];
198216
for k in 0..64 {
199217
newdata[j * 64 + k] = v[k];
200218
}
201219
}
202-
for j in 0..mix.len() {
203-
mix[j] = fnv(
204-
mix[j] as u32,
205-
u8s_to_u32(&newdata[j * 32 .. (j + 1) * 32])
206-
) as u8;
207-
}
220+
mix = fnv128(mix, newdata);
208221
}
209-
let mut cmix = [0u8; MIX_BYTES];
210-
for i in 0..(MIX_BYTES / 4) {
211-
cmix[i] = fnv(fnv(fnv(mix[i] as u32, mix[i + 1] as u32),
212-
mix[i + 2] as u32), mix[i + 3] as u32) as u8;
222+
let mut cmix = [0u8; MIX_BYTES / 4];
223+
for i in 0..(MIX_BYTES / 4 / 4) {
224+
let j = i * 4;
225+
let a = fnv((&mix[(j * 4)..]).read_u32::<LittleEndian>().unwrap(),
226+
(&mix[((j + 1) * 4)..]).read_u32::<LittleEndian>().unwrap());
227+
let b = fnv(a, (&mix[((j + 2) * 4)..]).read_u32::<LittleEndian>().unwrap());
228+
let c = fnv(b, (&mix[((j + 3) * 4)..]).read_u32::<LittleEndian>().unwrap());
229+
230+
(&mut cmix[j..]).write_u32::<LittleEndian>(c);
213231
}
214232
let result = {
215233
let mut hasher = Keccak256::default();
@@ -222,28 +240,28 @@ pub fn hashimoto<F: Fn(usize) -> H512>(
222240
}
223241
z
224242
};
225-
(H1024::from(cmix), H256::from(result))
243+
(H256::from(cmix), H256::from(result))
226244
}
227245

228246
/// Ethash used by a light client. Only stores the 16MB cache rather than the
229247
/// full dataset.
230248
pub fn hashimoto_light<T: Encodable>(
231249
header: &T, nonce: H64, full_size: usize, cache: &[u8]
232-
) -> (H1024, H256) {
250+
) -> (H256, H256) {
233251
let header = rlp::encode(header).to_vec();
234252

235-
hashimoto(&header, nonce, full_size, |i| {
253+
hashimoto(H256::from(Keccak256::digest(&header).as_slice()), nonce, full_size, |i| {
236254
calc_dataset_item(cache, i)
237255
})
238256
}
239257

240258
/// Ethash used by a full client. Stores the whole dataset in memory.
241259
pub fn hashimoto_full<T: Encodable>(
242260
header: &T, nonce: H64, full_size: usize, dataset: &[u8]
243-
) -> (H1024, H256) {
261+
) -> (H256, H256) {
244262
let header = rlp::encode(header).to_vec();
245263

246-
hashimoto(&header, nonce, full_size, |i| {
264+
hashimoto(H256::from(Keccak256::digest(&header).as_slice()), nonce, full_size, |i| {
247265
let mut r = [0u8; 64];
248266
for j in 0..64 {
249267
r[j] = dataset[i * 64 + j];
@@ -262,7 +280,7 @@ pub fn mine<T: Encodable>(
262280

263281
let mut nonce_current = nonce_start;
264282
loop {
265-
let (_, result) = hashimoto(&header, nonce_current, full_size, |i| {
283+
let (_, result) = hashimoto(H256::from(Keccak256::digest(&header).as_slice()), nonce_current, full_size, |i| {
266284
let mut r = [0u8; 64];
267285
for j in 0..64 {
268286
r[j] = dataset[i * 64 + j];

tests/headers.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
extern crate ethash;
2+
extern crate block;
3+
extern crate rlp;
4+
extern crate hexutil;
5+
extern crate bigint;
6+
extern crate sha3;
7+
extern crate blockchain;
8+
9+
use ethash::*;
10+
use block::*;
11+
use hexutil::*;
12+
use bigint::*;
13+
use blockchain::chain::HeaderHash;
14+
use sha3::{Keccak256, Digest};
15+
16+
use std::str::FromStr;
17+
18+
#[test]
19+
fn header1() {
20+
let header: Header = rlp::decode(&read_hex("f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808080a058f759ede17a706c93f13030328bcea40c1d1341fb26f2facd21ceb0dae57017884242424242424242").unwrap());
21+
let partial = PartialHeader::from_full(header.clone());
22+
23+
assert_eq!(get_cache_size(header.number), 16776896);
24+
assert_eq!(get_full_size(header.number), 1073739904);
25+
assert_eq!(partial.header_hash(), H256::from_str("2a8de2adf89af77358250bf908bf04ba94a6e8c3ba87775564a41d269a05e4ce").unwrap());
26+
27+
let cache_size = get_cache_size(header.number);
28+
let full_size = get_full_size(header.number);
29+
30+
let mut cache: Vec<u8> = Vec::with_capacity(cache_size);
31+
cache.resize(cache_size, 0);
32+
33+
let seed = get_seedhash(header.number);
34+
assert_eq!(seed, H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap());
35+
36+
make_cache(&mut cache, seed);
37+
38+
let hash = H256::from(Keccak256::digest(&cache).as_slice());
39+
assert_eq!(hash, H256::from_str("35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353").unwrap());
40+
41+
let (mixhash, result) = hashimoto_light(&partial, H64::from("4242424242424242"),
42+
full_size, &cache);
43+
44+
assert_eq!(mixhash, H256::from("58f759ede17a706c93f13030328bcea40c1d1341fb26f2facd21ceb0dae57017"));
45+
assert_eq!(result, H256::from("dd47fd2d98db51078356852d7c4014e6a5d6c387c35f40e2875b74a256ed7906"));
46+
}
47+
48+
#[test]
49+
fn header2() {
50+
let header: Header = rlp::decode(&read_hex("f901f7a01bef91439a3e070a6586851c11e6fd79bbbea074b2b836727b8e75c7d4a6b698a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea3cb5f94fa2ddd52ec6dd6eb75cf824f4058ca1a00c6e51346be0670ce63ac5f05324e27d20b180146269c5aab844d09a2b108c64a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd880845511ed2a80a0e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d17588307692cf71b12f6d").unwrap());
51+
let partial = PartialHeader::from_full(header.clone());
52+
53+
assert_eq!(get_cache_size(header.number), 16776896);
54+
assert_eq!(get_full_size(header.number), 1073739904);
55+
assert_eq!(partial.header_hash(), H256::from_str("100cbec5e5ef82991290d0d93d758f19082e71f234cf479192a8b94df6da6bfe").unwrap());
56+
57+
let cache_size = get_cache_size(header.number);
58+
let full_size = get_full_size(header.number);
59+
60+
let mut cache: Vec<u8> = Vec::with_capacity(cache_size);
61+
cache.resize(cache_size, 0);
62+
63+
let seed = get_seedhash(header.number);
64+
assert_eq!(seed, H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap());
65+
66+
make_cache(&mut cache, seed);
67+
68+
let hash = H256::from(Keccak256::digest(&cache).as_slice());
69+
assert_eq!(hash, H256::from_str("35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353").unwrap());
70+
71+
let (mixhash, result) = hashimoto_light(&partial, H64::from("307692cf71b12f6d"),
72+
full_size, &cache);
73+
74+
assert_eq!(mixhash, H256::from("e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d175"));
75+
assert_eq!(result, H256::from("ab9b13423cface72cbec8424221651bc2e384ef0f7a560e038fc68c8d8684829"));
76+
}

0 commit comments

Comments
 (0)