fixed equihash

This commit is contained in:
Svyatoslav Nikolsky 2018-05-21 14:16:27 +03:00
parent d62342f3f1
commit 787a36549c
6 changed files with 109 additions and 10 deletions

View File

@ -5,6 +5,7 @@ use ser::{deserialize, serialize};
use crypto::dhash256;
use compact::Compact;
use hash::H256;
use primitives::bytes::Bytes;
use ser::{Error, Serializable, Deserializable, Stream, Reader};
#[derive(Debug, PartialEq, Default, Clone)]
@ -18,6 +19,7 @@ pub struct BlockHeader {
pub time: u32,
pub bits: Compact,
pub nonce: BlockHeaderNonce,
pub hash_final_sapling_root: Option<H256>,
pub equihash_solution: Option<EquihashSolution>,
}
@ -37,17 +39,44 @@ impl BlockHeader {
pub fn hash(&self) -> H256 {
dhash256(&serialize(self))
}
pub fn equihash_input(&self) -> Bytes {
let mut stream = Stream::new();
stream
.append(&self.version)
.append(&self.previous_header_hash)
.append(&self.merkle_root_hash);
if let Some(hash_final_sapling_root) = self.hash_final_sapling_root.as_ref() {
stream.append(hash_final_sapling_root);
}
stream
.append(&self.time)
.append(&self.bits);
match self.nonce {
BlockHeaderNonce::U32(ref v) => stream.append(v),
BlockHeaderNonce::H256(ref v) => stream.append(v),
};
stream.out()
}
}
impl fmt::Debug for BlockHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use rustc_serialize::hex::ToHex;
f.debug_struct("BlockHeader")
.field("version", &self.version)
.field("previous_header_hash", &self.previous_header_hash.reversed())
.field("merkle_root_hash", &self.merkle_root_hash.reversed())
.field("hash_final_sapling_root", &self.hash_final_sapling_root)
.field("time", &self.time)
.field("bits", &self.bits)
.field("nonce", &self.nonce)
.field("equihash_solution", &self.equihash_solution.as_ref().map(|s| s.0.to_hex()))
.finish()
}
}
@ -60,13 +89,12 @@ impl From<&'static str> for BlockHeader {
impl Serializable for BlockHeader {
fn serialize(&self, stream: &mut Stream) {
let is_zcash_format = stream.is_zcash_stream();
stream
.append(&self.version)
.append(&self.previous_header_hash)
.append(&self.merkle_root_hash);
if is_zcash_format {
stream.append(&H256::default());
if let Some(hash_final_sapling_root) = self.hash_final_sapling_root.as_ref() {
stream.append(hash_final_sapling_root);
}
stream
.append(&self.time)
@ -92,9 +120,11 @@ impl Deserializable for BlockHeader {
let merkle_root_hash = reader.read()?;
// TODO: rename to transaction format - original, witness, zcash, must be enum, not flags
if is_zcash_format {
let _reserved_hash: H256 = reader.read()?;
}
let hash_final_sapling_root = if is_zcash_format {
Some(reader.read()?)
} else {
None
};
let time = reader.read()?;
let bits = reader.read()?;
@ -113,6 +143,7 @@ impl Deserializable for BlockHeader {
version,
previous_header_hash,
merkle_root_hash,
hash_final_sapling_root,
time,
bits,
nonce,

View File

@ -55,6 +55,20 @@ impl Builder {
self
}
/// Push integer to the end of script
pub fn push_i64(mut self, int: i64) -> Self {
if int == -1 || (int >= 1 && int <= 16) {
let shift: i64 = (Opcode::OP_1 as u8 - 1) as i64;
self.data.push((int + shift) as u8);
self
} else if int == 0 {
self.data.push(Opcode::OP_0 as u8);
self
} else {
self.push_num(int.into())
}
}
/// Appends num push operation to the end of script
pub fn push_num(self, num: Num) -> Self {
self.push_data(&num.to_bytes())

View File

@ -269,7 +269,7 @@ impl<'a> BlockCoinbaseScript<'a> {
}
let prefix = script::Builder::default()
.push_num(self.height.into())
.push_i64(self.height.into())
.into_script();
let matches = self.block.transactions.first()

View File

@ -1,4 +1,4 @@
use network::ConsensusParams;
use network::{ConsensusParams, ConsensusFork};
use storage::BlockHeaderProvider;
use canon::CanonHeader;
use error::Error;
@ -8,6 +8,7 @@ use deployments::Deployments;
pub struct HeaderAcceptor<'a> {
pub version: HeaderVersion<'a>,
pub equihash: HeaderEquihashSolution<'a>,
pub work: HeaderWork<'a>,
pub median_timestamp: HeaderMedianTimestamp<'a>,
}
@ -22,6 +23,7 @@ impl<'a> HeaderAcceptor<'a> {
) -> Self {
let csv_active = deployments.as_ref().csv(height, store, consensus);
HeaderAcceptor {
equihash: HeaderEquihashSolution::new(header, consensus),
work: HeaderWork::new(header, store, height, consensus),
median_timestamp: HeaderMedianTimestamp::new(header, store, csv_active),
version: HeaderVersion::new(header, height, consensus),
@ -29,6 +31,7 @@ impl<'a> HeaderAcceptor<'a> {
}
pub fn check(&self) -> Result<(), Error> {
try!(self.equihash.check());
try!(self.version.check());
try!(self.work.check());
try!(self.median_timestamp.check());
@ -64,6 +67,39 @@ impl<'a> HeaderVersion<'a> {
}
}
pub struct HeaderEquihashSolution<'a> {
header: CanonHeader<'a>,
consensus: &'a ConsensusParams,
}
impl<'a> HeaderEquihashSolution<'a> {
fn new(header: CanonHeader<'a>, consensus: &'a ConsensusParams) -> Self {
HeaderEquihashSolution {
header: header,
consensus: consensus,
}
}
fn check(&self) -> Result<(), Error> {
match self.consensus.fork {
ConsensusFork::ZCash => (),
_ => return Ok(()),
}
use equihash;
let is_solution_correct = equihash::verify_block_equihash_solution(&equihash::EquihashParams {
N: 200,
K: 9,
}, &self.header.raw);
if is_solution_correct {
Ok(())
} else {
Err(Error::InvalidEquihashSolution)
}
}
}
pub struct HeaderWork<'a> {
header: CanonHeader<'a>,
store: &'a BlockHeaderProvider,

View File

@ -2,6 +2,7 @@
use blake2_rfc::blake2b::Blake2b;
use byteorder::{BigEndian, LittleEndian, ByteOrder, WriteBytesExt};
use chain::BlockHeader;
use primitives::hex::ToHex;
pub struct EquihashParams {
@ -39,6 +40,16 @@ impl EquihashParams {
}
}
pub fn verify_block_equihash_solution(params: &EquihashParams, header: &BlockHeader) -> bool {
let equihash_solution = match header.equihash_solution.as_ref() {
Some(equihash_solution) => equihash_solution,
None => return false,
};
let input = header.equihash_input();
verify_equihash_solution(params, &input, &equihash_solution.0)
}
pub fn verify_equihash_solution(params: &EquihashParams, input: &[u8], solution: &[u8]) -> bool {
if solution.len() != params.solution_size() {
return false;
@ -117,12 +128,18 @@ fn merge_rows(row1: &[u8], row2: &[u8], len: usize, indices_len: usize, trim: us
}
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 {
let mut i = 0;
let mut j = 0;
while i < indices_len {
while j < indices_len {
if row1[len + i..len + i + 4] == row2[len + j..len + j + 4] {
return false;
}
j += 4;
}
i += 4;
}
true

View File

@ -59,6 +59,7 @@ pub enum Error {
UnexpectedWitness,
/// Database error
Database(DBError),
InvalidEquihashSolution,
}
impl From<DBError> for Error {