From 70a8770f13aff77308675852c8e709c23bd74f56 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 17 May 2018 13:55:08 +0300 Subject: [PATCH] equihash working --- Cargo.lock | 47 ++-- test-data/src/block.rs | 2 + verification/Cargo.toml | 3 +- verification/src/accept_transaction.rs | 1 + verification/src/deployments.rs | 1 + verification/src/equihash.rs | 289 +++++++++++++++++++++++-- verification/src/lib.rs | 2 + verification/src/work_bch.rs | 2 + 8 files changed, 319 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f72c8d2..86e95b67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ name = "base64" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -69,7 +69,7 @@ dependencies = [ name = "bencher" version = "0.1.0" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "db 0.1.0", "network 0.1.0", @@ -85,7 +85,7 @@ name = "bigint" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -121,9 +121,19 @@ name = "bitflags" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "git+https://github.com/gtank/blake2-rfc.git?branch=persona#c7c458429c429b81fea845421f5ab859710fa8af" +dependencies = [ + "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byteorder" -version = "1.1.0" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -131,7 +141,7 @@ name = "bytes" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -181,6 +191,11 @@ dependencies = [ "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crossbeam-deque" version = "0.2.0" @@ -217,7 +232,7 @@ name = "csv" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -255,7 +270,7 @@ name = "domain" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -614,7 +629,7 @@ name = "message" version = "0.1.0" dependencies = [ "bitcrypto 0.1.0", - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "network 0.1.0", "primitives 0.1.0", @@ -635,7 +650,7 @@ name = "miner" version = "0.1.0" dependencies = [ "bitcrypto 0.1.0", - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "db 0.1.0", "heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -683,7 +698,7 @@ name = "murmur3" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -864,7 +879,7 @@ name = "primitives" version = "0.1.0" dependencies = [ "bigint 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1125,7 +1140,7 @@ dependencies = [ name = "serialization" version = "0.1.0" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", ] @@ -1221,7 +1236,7 @@ version = "0.1.0" dependencies = [ "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitcrypto 0.1.0", - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "db 0.1.0", "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1421,6 +1436,8 @@ name = "verification" version = "0.1.0" dependencies = [ "bitcrypto 0.1.0", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", "db 0.1.0", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1516,12 +1533,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)" = "" +"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" diff --git a/test-data/src/block.rs b/test-data/src/block.rs index 73f6257b..eca0abfa 100644 --- a/test-data/src/block.rs +++ b/test-data/src/block.rs @@ -248,6 +248,7 @@ impl BlockHeaderBuilder where F: Invoke { nonce: self.nonce, merkle_root_hash: self.merkle_root, version: self.version, + equihash_solution: None, } ) } @@ -335,6 +336,7 @@ impl TransactionBuilder where F: Invoke { version: self.version, inputs: self.inputs, outputs: self.outputs, + joint_split: None, } ) } diff --git a/verification/Cargo.toml b/verification/Cargo.toml index 6ff93216..17628a67 100644 --- a/verification/Cargo.toml +++ b/verification/Cargo.toml @@ -9,7 +9,8 @@ lazy_static = "1.0" log = "0.4" rayon = "1.0" parking_lot = "0.4" -blake2-rfc = { version = "0.2.18" } +blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", branch = "persona" } +byteorder = "1.2" primitives = { path = "../primitives" } chain = { path = "../chain" } serialization = { path = "../serialization" } diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index fb219514..6dc53e0b 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -506,6 +506,7 @@ mod tests { .into_bytes(), }], lock_time: 0xffffffff, + joint_split: None, }.into(); assert_eq!(transaction.raw.outputs[0].script_pubkey.len(), 46 + 2); diff --git a/verification/src/deployments.rs b/verification/src/deployments.rs index bc402b55..95ff5327 100644 --- a/verification/src/deployments.rs +++ b/verification/src/deployments.rs @@ -294,6 +294,7 @@ mod tests { time: time, bits: 0.into(), nonce: height, + equihash_solution: None, }; previous_header_hash = header.hash(); diff --git a/verification/src/equihash.rs b/verification/src/equihash.rs index 8451afcf..e39c105e 100644 --- a/verification/src/equihash.rs +++ b/verification/src/equihash.rs @@ -1,6 +1,8 @@ // https://github.com/zcash/zcash/commit/fdda3c5085199d2c2170887aa064fc42afdb0360 use blake2_rfc::blake2b::Blake2b; +use byteorder::{BigEndian, LittleEndian, ByteOrder, WriteBytesExt}; +use primitives::hex::ToHex; pub struct EquihashParams { pub N: u32, @@ -8,12 +10,32 @@ pub struct EquihashParams { } impl EquihashParams { + pub fn indices_per_hash_output(&self) -> usize { + (512 / self.N) as usize + } + + pub fn hash_output(&self) -> usize { + (self.indices_per_hash_output() * self.N as usize / 8usize) as usize + } + pub fn collision_bit_length(&self) -> usize { - self.N / (self.K + 1) + (self.N / (self.K + 1)) as usize + } + + pub fn collision_byte_length(&self) -> usize { + (self.collision_bit_length() + 7) / 8 + } + + pub fn final_full_width(&self) -> usize { + 2 * self.collision_byte_length() + 4 * (1 << self.K) } pub fn solution_size(&self) -> usize { - (1usize << self.K) * (self.collision_bit_length() + 1) / 8 + ((1usize << self.K) * (self.collision_bit_length() + 1) / 8) as usize + } + + pub fn hash_length(&self) -> usize { + (self.K as usize + 1) * self.collision_byte_length() } } @@ -22,32 +44,273 @@ pub fn verify_equihash_solution(params: &EquihashParams, input: &[u8], solution: return false; } - let mut context = Blake2b::new(64); + let mut context = new_blake2(params); context.update(input); + + // pure equihash + + let collision_bit_length = params.collision_bit_length(); + let indices = get_indices_from_minimal(solution, collision_bit_length); + + let mut rows = Vec::new(); + for idx in indices { + let hash = generate_hash(&context, (idx as usize / params.indices_per_hash_output()) as u32); + let hash_begin = (idx as usize % params.indices_per_hash_output()) * params.N as usize / 8; + let hash_end = hash_begin + params.N as usize / 8; + + let mut row = vec![0; params.final_full_width()]; + let expanded_hash = expand_array( + &hash[hash_begin..hash_end], + params.collision_bit_length(), + 0); + row[0..expanded_hash.len()].clone_from_slice(&expanded_hash); + row[params.hash_length()..params.hash_length() + 4].clone_from_slice(&to_big_endian(idx)); + rows.push(row); + } + + let mut hash_len = params.hash_length(); + let mut indices_len = 4; + while rows.len() > 1 { + let mut rows_check = Vec::new(); + for i in 0..rows.len() / 2 { + let row1 = &rows[i * 2]; + let row2 = &rows[i * 2 + 1]; + if !has_collision(row1, row2, params.collision_byte_length()) { + return false; + } + if indices_before(row2, row1, hash_len, indices_len) { + return false; + } + if !distinct_indices(row1, row2, hash_len, indices_len) { + return false; + } + rows_check.push(merge_rows(row1, row2, hash_len, indices_len, params.collision_byte_length())); + } + + rows = rows_check; + hash_len -= params.collision_byte_length(); + indices_len *= 2; + } + + rows[0].iter().take(hash_len).all(|x| *x == 0) +} + +fn merge_rows(row1: &[u8], row2: &[u8], len: usize, indices_len: usize, trim: usize) -> Vec { + let mut row = row1.to_vec(); + for i in trim..len { + row[i - trim] = row1[i] ^ row2[i]; + } + + if indices_before(row1, row2, len, indices_len) { + row[len - trim..len - trim + indices_len] + .clone_from_slice(&row1[len..len + indices_len]); + row[len - trim + indices_len..len - trim + indices_len + indices_len] + .clone_from_slice(&row2[len..len + indices_len]); + } else { + row[len - trim..len - trim + indices_len] + .clone_from_slice(&row2[len..len + indices_len]); + row[len - trim + indices_len..len - trim + indices_len + indices_len] + .clone_from_slice(&row1[len..len + indices_len]); + } + + row +} + +fn distinct_indices(row1: &[u8], row2: &[u8], len: usize, indices_len: usize) -> bool { + for i in 0..indices_len / 4 { + for j in 0..indices_len / 4 { + if row1[len + i..len + i + 4] == row2[len + j..len + j + 4] { + return false; + } + } + } + + true +} + +fn has_collision(row1: &[u8], row2: &[u8], collision_byte_length: usize) -> bool { + for i in 0..collision_byte_length { + if row1[i] != row2[i] { + return false; + } + } + + true +} + +fn indices_before(row1: &[u8], row2: &[u8], len: usize, indices_len: usize) -> bool { + for i in 0..indices_len { + if row1[len + i] < row2[len + i] { + return true; + } else if row1[len + i] > row2[len + i] { + return false; + } + } + + false +} + +fn generate_hash(context: &Blake2b, g: u32) -> Vec { + let mut context = context.clone(); + context.update(&to_little_endian(g)); + context.finalize().as_bytes().to_vec() } fn get_indices_from_minimal(solution: &[u8], collision_bit_length: usize) -> Vec { let indices_len = 8 * 4 * solution.len() / (collision_bit_length + 1); let byte_pad = 4 - ((collision_bit_length + 1 + 7) / 8); - let mut array = Vec::new(); + let array = expand_array(solution, collision_bit_length + 1, byte_pad); + + let mut ret = Vec::new(); + for i in 0..indices_len / 4 { + ret.push(array_to_eh_index(&array[i*4..i*4 + 4])); + } + ret } -fn expand_array(data: &[u8], array: &mut Vec, bit_len: usize, byte_pad: usize) { - +fn get_minimal_from_indices(indices: &[u32], collision_bit_length: usize) -> Vec { + let indices_len = indices.len() * 4; + let min_len = (collision_bit_length + 1) * indices_len / (8 * 4); + let byte_pad = 4 - ((collision_bit_length + 1) + 7) / 8; + + let mut array = Vec::new(); + for i in 0..indices.len() { + let mut be_index = Vec::new(); + be_index.write_u32::(indices[i]).unwrap(); + array.extend(be_index); + } + + let mut ret = vec![0u8; min_len]; + compress_array(&array, &mut ret, collision_bit_length + 1, byte_pad); + ret +} + +fn array_to_eh_index(data: &[u8]) -> u32 { + BigEndian::read_u32(data) +} + +fn expand_array(data: &[u8], bit_len: usize, byte_pad: usize) -> Vec { + let mut array = Vec::new(); + let out_width = (bit_len + 7) / 8 + byte_pad; + let bit_len_mask = (1u32 << bit_len) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits = 0usize; + let mut acc_value = 0u32; + + let mut j = 0usize; + for i in 0usize..data.len() { + acc_value = (acc_value << 8) | (data[i] as u32); + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if acc_bits >= bit_len { + acc_bits -= bit_len; + for x in 0usize..byte_pad { + array.push(0); + } + for x in byte_pad..out_width { + array.push(( + // Big-endian + (acc_value >> (acc_bits + (8 * (out_width - x - 1)))) as u8 + ) & ( + // Apply bit_len_mask across byte boundaries + ((bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF) as u8 + )); + } + j += out_width; + } + } + + array +} + +fn compress_array(data: &[u8], array: &mut Vec, bit_len: usize, byte_pad: usize) { + let in_width = (bit_len + 7) / 8 + byte_pad; + let bit_len_mask = (1u32 << bit_len) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits = 0usize; + let mut acc_value = 0u32; + + let mut j = 0usize; + for i in 0usize..array.len() { + // When we have fewer than 8 bits left in the accumulator, read the next + // input element. + if acc_bits < 8 { + acc_value = acc_value << bit_len; + for x in byte_pad..in_width { + acc_value = acc_value | (( + data[j + x] & (((bit_len_mask >> (8 * (in_width - x - 1))) & 0xFF) as u8) + ) as u32) << (8 * (in_width - x - 1)); + } + j += in_width; + acc_bits += bit_len; + } + + acc_bits -= 8; + array[i] = ((acc_value >> acc_bits) & 0xFF) as u8; + } +} + +fn new_blake2(params: &EquihashParams) -> Blake2b { + let mut personalization = [0u8; 16]; + personalization[0..8].clone_from_slice(b"ZcashPoW"); + personalization[8..12].clone_from_slice(&to_little_endian(params.N)); + personalization[12..16].clone_from_slice(&to_little_endian(params.K)); + Blake2b::with_params(params.hash_output(), &[], &[], &personalization) +} + +fn to_little_endian(num: u32) -> [u8; 4] { + let mut le_num = [0u8; 4]; + LittleEndian::write_u32(&mut le_num[..], num); + le_num +} + +fn to_big_endian(num: u32) -> [u8; 4] { + let mut be_num = [0u8; 4]; + BigEndian::write_u32(&mut be_num[..], num); + be_num } #[cfg(test)] mod tests { - fn test_equihash_verifier(n: u32, k: u32, input: &[u8], nonce: U256, solution: &[u32]) -> bool { + use primitives::bigint::{Uint, U256}; + use super::*; + fn test_equihash_verifier(n: u32, k: u32, input: &[u8], nonce: U256, solution: &[u32]) -> bool { + let solution = get_minimal_from_indices(solution, (n / (k + 1)) as usize); +/* + + ZCash (reset && BOOST_TEST_LOG_LEVEL=message ./src/test/test_bitcoin --run_test=equihash_tests/validator_testvectors): + + + pbtc: + + + +*/ + + let mut le_nonce = vec![0; 32]; + nonce.to_little_endian(&mut le_nonce); + let mut input = input.to_vec(); + input.extend(le_nonce); + + let params = EquihashParams { N: n, K: k }; + + verify_equihash_solution(¶ms, &input, &solution) } - void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I, const arith_uint256 &nonce, std::vector soln, bool expected) { #[test] fn verify_equihash_solution_works() { - test_equihash_verifier( - 96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", - U256::one(), - ); + assert!(test_equihash_verifier( + 96, 5, b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + U256::one(), &vec![ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + )); } -} \ No newline at end of file +} diff --git a/verification/src/lib.rs b/verification/src/lib.rs index 147e6e03..3ff62677 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -59,6 +59,7 @@ extern crate log; extern crate parking_lot; extern crate rayon; extern crate blake2_rfc; +extern crate byteorder; extern crate storage; extern crate chain; @@ -74,6 +75,7 @@ pub mod constants; mod canon; mod deployments; mod duplex_store; +mod equihash; mod error; mod sigops; mod timestamp; diff --git a/verification/src/work_bch.rs b/verification/src/work_bch.rs index 3e1b1d14..e78329f1 100644 --- a/verification/src/work_bch.rs +++ b/verification/src/work_bch.rs @@ -227,6 +227,7 @@ mod tests { time: 1269211443, bits: 0x207fffff.into(), nonce: 0, + equihash_solution: None, }); // create x100 pre-HF blocks @@ -286,6 +287,7 @@ mod tests { time: 1269211443, bits: initial_bits.into(), nonce: 0, + equihash_solution: None, }); // Pile up some blocks every 10 mins to establish some history.