header verification - completed
This commit is contained in:
parent
1ff4fbc2ec
commit
a2ac7a6a80
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(¶ms, &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(¶ms, &input, &solution)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue