Check PoWLimit for difficulty threshold

This commit is contained in:
teor 2020-10-13 08:17:40 +10:00
parent 00de709dd8
commit c3a8fd3894
4 changed files with 50 additions and 8 deletions

View File

@ -11,7 +11,7 @@
//! the actual work represented by the block header hash.
#![allow(clippy::unit_arg)]
use crate::block;
use crate::{block, parameters::Network};
use std::cmp::{Ordering, PartialEq, PartialOrd};
use std::{fmt, ops::Add, ops::AddAssign};
@ -257,6 +257,21 @@ impl ExpandedDifficulty {
/// users of this module should avoid converting hashes into difficulties.
fn from_hash(hash: &block::Hash) -> ExpandedDifficulty {
U256::from_little_endian(&hash.0).into()
}
/// Returns the easiest target difficulty allowed on `network`.
///
/// See `PoWLimit` in the Zcash specification.
pub fn target_difficulty_limit(network: Network) -> ExpandedDifficulty {
let limit: U256 = match network {
/* 2^243 - 1 */
Network::Mainnet => (U256::one() << 243) - 1,
/* 2^251 - 1 */
Network::Testnet => (U256::one() << 251) - 1,
};
limit.into()
}
}
impl From<U256> for ExpandedDifficulty {
@ -277,6 +292,9 @@ impl PartialEq<block::Hash> for ExpandedDifficulty {
impl PartialOrd<block::Hash> for ExpandedDifficulty {
/// `block::Hash`es are compared with `ExpandedDifficulty` thresholds by
/// converting the hash to a 256-bit integer in little-endian order.
///
/// Greater values represent *less* work. This matches the convention in
/// zcashd and bitcoin.
fn partial_cmp(&self, other: &block::Hash) -> Option<Ordering> {
self.partial_cmp(&ExpandedDifficulty::from_hash(other))
}

View File

@ -140,7 +140,7 @@ where
// Do the difficulty checks first, to raise the threshold for
// attacks that use any other fields.
check::difficulty_is_valid(&block.header, &height, &hash)?;
check::difficulty_is_valid(&block.header, network, &height, &hash)?;
check::equihash_solution_is_valid(&block.header)?;
// Since errors cause an early exit, try to do the

View File

@ -7,7 +7,7 @@ use zebra_chain::{
block::Height,
block::{Block, Header},
parameters::{Network, NetworkUpgrade},
work::equihash,
work::{difficulty::ExpandedDifficulty, equihash},
};
use crate::{error::*, parameters::SLOW_START_INTERVAL};
@ -38,23 +38,38 @@ pub fn coinbase_is_first(block: &Block) -> Result<(), BlockError> {
Ok(())
}
/// Returns `Ok(())` if `hash` passes the difficulty filter and PoW limit,
/// Returns `Ok(())` if `hash` passes:
/// - the target difficulty limit for `network` (PoWLimit), and
/// - the difficulty filter,
/// based on the fields in `header`.
///
/// If the block is invalid, returns an error containing `height` and `hash`.
pub fn difficulty_is_valid(
header: &Header,
network: Network,
height: &Height,
hash: &Hash,
) -> Result<(), BlockError> {
// TODO:
// - PoW limit
let difficulty_threshold = header
.difficulty_threshold
.to_expanded()
.ok_or(BlockError::InvalidDifficulty(*height, *hash))?;
// Note: the comparisons in this function are u256 integer comparisons, like
// zcashd and bitcoin. Greater values represent *less* work.
// The PowLimit check is part of `Threshold()` in the spec, but it doesn't
// actually depend on any previous blocks.
if difficulty_threshold > ExpandedDifficulty::target_difficulty_limit(network) {
Err(BlockError::TargetDifficultyLimit(
*height,
*hash,
difficulty_threshold,
network,
ExpandedDifficulty::target_difficulty_limit(network),
))?;
}
// Difficulty filter
if hash > &difficulty_threshold {
Err(BlockError::DifficultyFilter(

View File

@ -53,7 +53,16 @@ pub enum BlockError {
#[error("invalid difficulty threshold in block header {0:?} {1:?}")]
InvalidDifficulty(zebra_chain::block::Height, zebra_chain::block::Hash),
#[error("block {0:?} failed the difficulty filter: hash {1:?} must be less than or equal to the difficulty threshold {2:?}")]
#[error("block {0:?} failed the difficulty limit: the difficulty threshold {2:?} must be at least as difficult as the {3:?} difficulty limit {4:?}, block hash {1:?}")]
TargetDifficultyLimit(
zebra_chain::block::Height,
zebra_chain::block::Hash,
zebra_chain::work::difficulty::ExpandedDifficulty,
zebra_chain::parameters::Network,
zebra_chain::work::difficulty::ExpandedDifficulty,
),
#[error("block {0:?} failed the difficulty filter: hash {1:?} must be at least as difficult as the difficulty threshold {2:?}")]
DifficultyFilter(
zebra_chain::block::Height,
zebra_chain::block::Hash,