header verification - completed

This commit is contained in:
Svyatoslav Nikolsky 2018-11-16 12:10:52 +03:00
parent 1ff4fbc2ec
commit a2ac7a6a80
7 changed files with 144 additions and 64 deletions

View File

@ -39,6 +39,9 @@ pub struct ConsensusParams {
pub pow_max_adjust_up: u32,
/// Optimal blocks interval (in seconds).
pub pow_target_spacing: u32,
/// Equihash (N, K) parameters.
pub equihash_params: Option<(u32, u32)>,
}
impl ConsensusParams {
@ -61,6 +64,8 @@ impl ConsensusParams {
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
equihash_params: Some((200, 9)),
},
Network::Testnet => ConsensusParams {
network: network,
@ -79,8 +84,10 @@ impl ConsensusParams {
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
equihash_params: Some((200, 9)),
},
Network::Regtest | Network::Unitest => ConsensusParams {
Network::Regtest => ConsensusParams {
network: network,
bip16_time: 0,
bip34_height: 100000000,
@ -97,6 +104,28 @@ impl ConsensusParams {
pow_max_adjust_down: 0,
pow_max_adjust_up: 0,
pow_target_spacing: (2.5 * 60.0) as u32,
equihash_params: Some((200, 9)),
},
Network::Unitest => ConsensusParams {
network: network,
bip16_time: 0,
bip34_height: 100000000,
bip65_height: 0,
bip66_height: 0,
rule_change_activation_threshold: 108, // 75%
miner_confirmation_window: 144,
csv_deployment: None,
overwinter_height: ::std::u32::MAX,
sapling_height: ::std::u32::MAX,
pow_averaging_window: 17,
pow_max_adjust_down: 0,
pow_max_adjust_up: 0,
pow_target_spacing: (2.5 * 60.0) as u32,
equihash_params: None,
},
}
}
@ -117,6 +146,10 @@ impl ConsensusParams {
(self.averaging_window_timespan() * (100 + self.pow_max_adjust_down)) / 100
}
pub fn min_block_version(&self) -> u32 {
4
}
pub fn max_block_size(&self) -> usize {
2_000_000
}

View File

@ -214,6 +214,11 @@ impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
}
}
pub fn version(mut self, version: u32) -> Self {
self.version = version;
self
}
pub fn parent(mut self, parent: H256) -> Self {
self.parent = parent;
self

View File

@ -1,4 +1,4 @@
use network::{Network, ConsensusParams};
use network::ConsensusParams;
use storage::BlockHeaderProvider;
use canon::CanonHeader;
use error::Error;
@ -8,7 +8,6 @@ use deployments::Deployments;
pub struct HeaderAcceptor<'a> {
pub version: HeaderVersion<'a>,
pub equihash: HeaderEquihashSolution<'a>,
pub work: HeaderWork<'a>,
pub median_timestamp: HeaderMedianTimestamp<'a>,
}
@ -23,7 +22,6 @@ 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),
@ -31,7 +29,6 @@ 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());
@ -67,39 +64,6 @@ 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.network {
Network::Unitest => 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

@ -7,21 +7,21 @@ use chain::BlockHeader;
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct EquihashParams {
pub N: u32,
pub K: u32,
pub n: u32,
pub k: u32,
}
impl EquihashParams {
pub fn indices_per_hash_output(&self) -> usize {
(512 / self.N) as usize
(512 / self.n) as usize
}
pub fn hash_output(&self) -> usize {
(self.indices_per_hash_output() * self.N as usize / 8usize) as 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)) as usize
(self.n / (self.k + 1)) as usize
}
pub fn collision_byte_length(&self) -> usize {
@ -29,22 +29,24 @@ impl EquihashParams {
}
pub fn final_full_width(&self) -> usize {
2 * self.collision_byte_length() + 4 * (1 << self.K)
2 * self.collision_byte_length() + 4 * (1 << self.k)
}
pub fn solution_size(&self) -> usize {
((1usize << self.K) * (self.collision_bit_length() + 1) / 8) as usize
((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()
(self.k as usize + 1) * self.collision_byte_length()
}
}
pub fn verify_block_equihash_solution(params: &EquihashParams, header: &BlockHeader) -> bool {
pub fn verify_block_equihash_solution(params: (u32, u32), header: &BlockHeader) -> bool {
let (n, k) = params;
let params = EquihashParams { n, k };
let equihash_solution = header.solution.as_ref();
let input = header.equihash_input();
verify_equihash_solution(params, &input, equihash_solution)
verify_equihash_solution(&params, &input, equihash_solution)
}
pub fn verify_equihash_solution(params: &EquihashParams, input: &[u8], solution: &[u8]) -> bool {
@ -63,8 +65,8 @@ pub fn verify_equihash_solution(params: &EquihashParams, input: &[u8], solution:
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 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(
@ -225,8 +227,8 @@ fn expand_array(data: &[u8], bit_len: usize, byte_pad: usize) -> Vec<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));
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)
}
@ -304,7 +306,7 @@ mod tests {
let mut input = input.to_vec();
input.extend(le_nonce);
let params = EquihashParams { N: n, K: k };
let params = EquihashParams { n, k };
verify_equihash_solution(&params, &input, &solution)
}

View File

@ -55,7 +55,10 @@ pub enum Error {
NonCanonicalTransactionOrdering,
/// Database error
Database(DBError),
/// Invalid equihash solution
InvalidEquihashSolution,
/// Invalid block version
InvalidVersion,
}
impl From<DBError> for Error {

View File

@ -29,13 +29,13 @@ impl<'a> BlockVerifier<'a> {
}
pub fn check(&self) -> Result<(), Error> {
try!(self.empty.check());
try!(self.coinbase.check());
try!(self.serialized_size.check());
try!(self.extra_coinbases.check());
try!(self.transactions_uniqueness.check());
try!(self.sigops.check());
try!(self.merkle_root.check());
self.empty.check()?;
self.coinbase.check()?;
self.serialized_size.check()?;
self.extra_coinbases.check()?;
self.transactions_uniqueness.check()?;
self.sigops.check()?;
self.merkle_root.check()?;
Ok(())
}
}
@ -193,4 +193,3 @@ impl<'a> BlockMerkleRoot<'a> {
}
}
}

View File

@ -1,26 +1,33 @@
use primitives::compact::Compact;
use chain::IndexedBlockHeader;
use equihash::verify_block_equihash_solution;
use network::ConsensusParams;
use work::is_valid_proof_of_work;
use error::Error;
use constants::BLOCK_MAX_FUTURE;
pub struct HeaderVerifier<'a> {
pub version: HeaderVersion<'a>,
pub equihash: HeaderEquihashSolution<'a>,
pub proof_of_work: HeaderProofOfWork<'a>,
pub timestamp: HeaderTimestamp<'a>,
}
impl<'a> HeaderVerifier<'a> {
pub fn new(header: &'a IndexedBlockHeader, consensus: &ConsensusParams, current_time: u32) -> Self {
pub fn new(header: &'a IndexedBlockHeader, consensus: &'a ConsensusParams, current_time: u32) -> Self {
HeaderVerifier {
version: HeaderVersion::new(header, consensus),
proof_of_work: HeaderProofOfWork::new(header, consensus),
equihash: HeaderEquihashSolution::new(header, consensus),
timestamp: HeaderTimestamp::new(header, current_time, BLOCK_MAX_FUTURE as u32),
}
}
pub fn check(&self) -> Result<(), Error> {
try!(self.proof_of_work.check());
try!(self.timestamp.check());
self.version.check()?;
self.equihash.check()?;
self.proof_of_work.check()?;
self.timestamp.check()?;
Ok(())
}
}
@ -70,3 +77,70 @@ impl<'a> HeaderTimestamp<'a> {
}
}
}
pub struct HeaderVersion<'a> {
header: &'a IndexedBlockHeader,
min_version: u32,
}
impl<'a> HeaderVersion<'a> {
fn new(header: &'a IndexedBlockHeader, consensus: &'a ConsensusParams) -> Self {
HeaderVersion {
header,
min_version: consensus.min_block_version(),
}
}
fn check(&self) -> Result<(), Error> {
if self.header.raw.version < self.min_version {
return Err(Error::InvalidVersion);
}
Ok(())
}
}
pub struct HeaderEquihashSolution<'a> {
header: &'a IndexedBlockHeader,
equihash_params: Option<(u32, u32)>,
}
impl<'a> HeaderEquihashSolution<'a> {
fn new(header: &'a IndexedBlockHeader, consensus: &'a ConsensusParams) -> Self {
HeaderEquihashSolution {
header,
equihash_params: consensus.equihash_params,
}
}
fn check(&self) -> Result<(), Error> {
if let Some(equihash_params) = self.equihash_params {
if !verify_block_equihash_solution(equihash_params, &self.header.raw) {
return Err(Error::InvalidEquihashSolution);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate test_data;
use network::{Network, ConsensusParams};
use error::Error;
use super::HeaderVersion;
#[test]
fn header_version_works() {
let consensus = ConsensusParams::new(Network::Mainnet);
assert_eq!(HeaderVersion::new(&test_data::block_builder().header().version(consensus.min_block_version() - 1)
.build().build().block_header.into(), &consensus).check(), Err(Error::InvalidVersion));
assert_eq!(HeaderVersion::new(&test_data::block_builder().header().version(consensus.min_block_version())
.build().build().block_header.into(), &consensus).check(), Ok(()));
assert_eq!(HeaderVersion::new(&test_data::block_builder().header().version(consensus.min_block_version() + 1)
.build().build().block_header.into(), &consensus).check(), Ok(()));
}
}