equihash working

This commit is contained in:
Svyatoslav Nikolsky 2018-05-17 13:55:08 +03:00
parent 4365a54099
commit 70a8770f13
8 changed files with 319 additions and 28 deletions

47
Cargo.lock generated
View File

@ -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)" = "<none>"
"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"

View File

@ -248,6 +248,7 @@ impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
nonce: self.nonce,
merkle_root_hash: self.merkle_root,
version: self.version,
equihash_solution: None,
}
)
}
@ -335,6 +336,7 @@ impl<F> TransactionBuilder<F> where F: Invoke<chain::Transaction> {
version: self.version,
inputs: self.inputs,
outputs: self.outputs,
joint_split: None,
}
)
}

View File

@ -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" }

View File

@ -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);

View File

@ -294,6 +294,7 @@ mod tests {
time: time,
bits: 0.into(),
nonce: height,
equihash_solution: None,
};
previous_header_hash = header.hash();

View File

@ -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<u8> {
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<u8> {
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<u32> {
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<u8>, bit_len: usize, byte_pad: usize) {
fn get_minimal_from_indices(indices: &[u32], collision_bit_length: usize) -> Vec<u8> {
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::<BigEndian>(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<u8> {
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<u8>, 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(&params, &input, &solution)
}
void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I, const arith_uint256 &nonce, std::vector<uint32_t> 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,
],
));
}
}
}

View File

@ -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;

View File

@ -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.