From cf45a61070749c3e711c94cbe2d1c25d55bd4507 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 31 Aug 2020 13:58:55 -0400 Subject: [PATCH] Set Params::pow_limit to an attainable value not a theoretical one `cloudhead` on IRC was asking how to properly use `BlockHeader::validate_pow()` on genesis (or similar) when the pow_limit field isn't expressible as a compact target (and, thus, does not actually represent the PoW limit/genesis target). We swap it for the actual PoW limit by truncating the way a compact encoding round-trip would. Note that, in Bitcoin Core, the original value is only ever used once in its original form: ``` if (bnNew > bnPowLimit) // Note: bnPowLimit is params.powLimit bnNew = bnPowLimit; return bnNew.GetCompact(); ``` Thus, even if Core adopted our change, as long as there exist no 256-bit integer x which satisfies `x <= powLimit && x > encoding_roundtrip_truncated(powLimit)` and `enoding_roundtrip_truncated(x) != powLimit`, the change would have no impact on consensus. It is trivial to show that there are no values which are between the new value (0x00000000ffff0000000000000000000000000000000000000000000000000000) and the original value (0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff) which can be encoded in compact form, but it is also critically, no such values will encode to a compact form of anything different than the new value as the encoding always truncates the low bits, never rounding up. --- src/consensus/params.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/consensus/params.rs b/src/consensus/params.rs index a1311fa1..c31bd16e 100644 --- a/src/consensus/params.rs +++ b/src/consensus/params.rs @@ -20,26 +20,26 @@ use network::constants::Network; use util::uint::Uint256; -/// Lowest possible difficulty for Mainnet. +/// Lowest possible difficulty for Mainnet. See comment on Params::pow_limit for more info. const MAX_BITS_BITCOIN: Uint256 = Uint256([ - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0x00000000ffffffffu64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x00000000ffff0000u64, ]); -/// Lowest possible difficulty for Testnet. +/// Lowest possible difficulty for Testnet. See comment on Params::pow_limit for more info. const MAX_BITS_TESTNET: Uint256 = Uint256([ - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0x00000000ffffffffu64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x00000000ffff0000u64, ]); -/// Lowest possible difficulty for Regtest. +/// Lowest possible difficulty for Regtest. See comment on Params::pow_limit for more info. const MAX_BITS_REGTEST: Uint256 = Uint256([ - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0x7fffffffffffffffu64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x7fffff0000000000u64, ]); #[derive(Debug, Clone)] @@ -62,6 +62,13 @@ pub struct Params { /// Number of blocks with the same set of rules. pub miner_confirmation_window: u32, /// Proof of work limit value. It contains the lowest possible difficulty. + /// + /// Note that this value differs from Bitcoin Core's powLimit field in that this value is + /// attainable, but Bitcoin Core's is not. Specifically, because targets in Bitcoin are always + /// rounded to the nearest float expressible in "compact form", not all targets are attainable. + /// Still, this should not affect consensus as the only place where the non-compact form of + /// this is used in Bitcoin Core's consensus algorithm is in comparison and there are no + /// compact-expressible values between Bitcoin Core's and the limit expressed here. pub pow_limit: Uint256, /// Expected amount of time to mine one block. pub pow_target_spacing: u64,