From 2a6821b4260e16fb5de5d49845f614bea530b519 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 22 Feb 2024 08:06:46 +1100 Subject: [PATCH 1/8] Use link to CompactTarget in rustdoc --- bitcoin/src/pow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index b5981080..e4f85b03 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -126,7 +126,7 @@ impl Target { /// The maximum **attainable** target value on mainnet. /// /// Not all target values are attainable because consensus code uses the compact format to - /// represent targets (see `CompactTarget`). + /// represent targets (see [`CompactTarget`]). pub const MAX_ATTAINABLE_MAINNET: Self = Target(U256(0xFFFF_u128 << (208 - 128), 0)); /// The proof of work limit on testnet. From 3d01146374ea2fd31c147da64871cedbc0c02051 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 20 Mar 2024 10:53:35 +1100 Subject: [PATCH 2/8] Allow needless-borrows-for-generic-args This lint triggers when parsing a reference to a large struct as a generic argument, which is wrong. Allow it crate wide because [subjectively] this lint never warns for anything useful. --- bitcoin/src/lib.rs | 1 + hashes/src/lib.rs | 1 + internals/src/lib.rs | 1 + io/src/lib.rs | 1 + units/src/lib.rs | 1 + 5 files changed, 5 insertions(+) diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index c86ed6a3..d264e37d 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -39,6 +39,7 @@ // Exclude lints we don't think are valuable. #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::needless_borrows_for_generic_args)] // https://github.com/rust-lang/rust-clippy/issues/12454 // Disable 16-bit support at least for now as we can't guarantee it yet. #[cfg(target_pointer_width = "16")] diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index e45fe9b2..9a83afa0 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -76,6 +76,7 @@ // Exclude lints we don't think are valuable. #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::needless_borrows_for_generic_args)] // https://github.com/rust-lang/rust-clippy/issues/12454 #[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; diff --git a/internals/src/lib.rs b/internals/src/lib.rs index ef31a0e7..bcbd3f26 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -14,6 +14,7 @@ // Exclude lints we don't think are valuable. #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::needless_borrows_for_generic_args)] // https://github.com/rust-lang/rust-clippy/issues/12454 #[cfg(feature = "alloc")] extern crate alloc; diff --git a/io/src/lib.rs b/io/src/lib.rs index 373e7a05..93143f14 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -16,6 +16,7 @@ // Exclude lints we don't think are valuable. #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::needless_borrows_for_generic_args)] // https://github.com/rust-lang/rust-clippy/issues/12454 #[cfg(feature = "alloc")] extern crate alloc; diff --git a/units/src/lib.rs b/units/src/lib.rs index f52176b2..0da62a64 100644 --- a/units/src/lib.rs +++ b/units/src/lib.rs @@ -11,6 +11,7 @@ // Exclude lints we don't think are valuable. #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::manual_range_contains)] // More readable than clippy's format. +#![allow(clippy::needless_borrows_for_generic_args)] // https://github.com/rust-lang/rust-clippy/issues/12454 #![no_std] // Disable 16-bit support at least for now as we can't guarantee it yet. From c1ba496a0746861a78ea344318b3bbf9d34d2cd6 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 22 Feb 2024 08:07:28 +1100 Subject: [PATCH 3/8] Document current behaviour of difficulty_float Improve rustdocs on the `Target::difficulty_float` function, specifically the return value if self is zero. --- bitcoin/src/pow.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index e4f85b03..e99aead2 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -245,6 +245,10 @@ impl Target { /// /// See [`difficulty`] for details. /// + /// # Returns + /// + /// Returns [`f64::INFINITY`] if `self` is zero (caused by divide by zero). + /// /// [`difficulty`]: Target::difficulty #[cfg_attr(all(test, mutate), mutate)] pub fn difficulty_float(&self) -> f64 { TARGET_MAX_F64 / self.0.to_f64() } From 104dee9376ea7abdc16b39eab74317af19a359e9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 29 Jan 2024 07:28:04 +1100 Subject: [PATCH 4/8] Debug assert that target != zero in difficulty calc The `difficulty` calculation requires dividing a target value by `self`. Add an assertion that `self` is not zero to help devs debug this. Note that this should never really be hit, but its possible there is a bug somewhere causing the target to be set to zero - so this may help debugging. Also, add panics section to rustdocs. --- bitcoin/src/pow.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index e99aead2..6292a822 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -227,10 +227,17 @@ impl Target { /// integer but `difficulty()` returns only 128 bits this means for targets below approximately /// `0xffff_ffff_ffff_ffff_ffff_ffff` `difficulty()` will saturate at `u128::MAX`. /// + /// # Panics + /// + /// Panics if `self` is zero (divide by zero). + /// /// [max]: Target::max /// [target]: crate::blockdata::block::Header::target #[cfg_attr(all(test, mutate), mutate)] pub fn difficulty(&self, network: Network) -> u128 { + // Panic here may be eaiser to debug than during the actual division. + assert_ne!(self.0, U256::ZERO, "divide by zero"); + let max = match network { Network::Bitcoin => Target::MAX_ATTAINABLE_MAINNET, Network::Testnet => Target::MAX_ATTAINABLE_TESTNET, From f0f6d3f1627cd3ebb0ae12bfc92690b102168824 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 30 Jan 2024 07:48:50 +1100 Subject: [PATCH 5/8] Take Params instead of Network in difficulty function What we really want is the maximum target, but since this is a const in `Params` use an `AsRef` argument in the `difficulty` functions. Requires implementation of `AsRef for Params`. --- bitcoin/src/blockdata/block.rs | 17 ++++++++++------- bitcoin/src/consensus/params.rs | 4 ++++ bitcoin/src/pow.rs | 11 ++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 504813bf..ebdbfa34 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -16,11 +16,11 @@ use io::{BufRead, Write}; use super::Weight; use crate::blockdata::script; use crate::blockdata::transaction::{Transaction, Txid, Wtxid}; -use crate::consensus::{encode, Decodable, Encodable}; +use crate::consensus::{encode, Decodable, Encodable, Params}; use crate::internal_macros::{impl_consensus_encoding, impl_hashencode}; use crate::pow::{CompactTarget, Target, Work}; use crate::prelude::*; -use crate::{merkle_tree, Network, VarInt}; +use crate::{merkle_tree, VarInt}; hashes::hash_newtype! { /// A bitcoin block hash. @@ -93,7 +93,9 @@ impl Header { /// /// Difficulty represents how difficult the current target makes it to find a block, relative to /// how difficult it would be at the highest possible target (highest target == lowest difficulty). - pub fn difficulty(&self, network: Network) -> u128 { self.target().difficulty(network) } + pub fn difficulty(&self, params: impl AsRef) -> u128 { + self.target().difficulty(params) + } /// Computes the popular "difficulty" measure for mining and returns a float value of f64. pub fn difficulty_float(&self) -> f64 { self.target().difficulty_float() } @@ -488,6 +490,7 @@ mod tests { use super::*; use crate::consensus::encode::{deserialize, serialize}; + use crate::Network; #[test] fn test_coinbase_and_bip34() { @@ -510,7 +513,7 @@ mod tests { #[test] fn block_test() { - let network = Network::Bitcoin; + let params = Params::new(Network::Bitcoin); // Mainnet block 00000000b0c5a240b2a61d2e75692224efd4cbecdf6eaf4cc2cf477ca7c270e7 let some_block = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000"); let cutoff_block = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac"); @@ -537,7 +540,7 @@ mod tests { real_decode.header.validate_pow(real_decode.header.target()).unwrap(), real_decode.block_hash() ); - assert_eq!(real_decode.header.difficulty(network), 1); + assert_eq!(real_decode.header.difficulty(¶ms), 1); assert_eq!(real_decode.header.difficulty_float(), 1.0); assert_eq!(real_decode.total_size(), some_block.len()); @@ -556,7 +559,7 @@ mod tests { // Check testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b #[test] fn segwit_block_test() { - let network = Network::Testnet; + let params = Params::new(Network::Testnet); let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw").to_vec(); let decode: Result = deserialize(&segwit_block); @@ -579,7 +582,7 @@ mod tests { real_decode.header.validate_pow(real_decode.header.target()).unwrap(), real_decode.block_hash() ); - assert_eq!(real_decode.header.difficulty(network), 2456598); + assert_eq!(real_decode.header.difficulty(¶ms), 2456598); assert_eq!(real_decode.header.difficulty_float(), 2456598.4399242126); assert_eq!(real_decode.total_size(), segwit_block.len()); diff --git a/bitcoin/src/consensus/params.rs b/bitcoin/src/consensus/params.rs index 08e69e68..55655384 100644 --- a/bitcoin/src/consensus/params.rs +++ b/bitcoin/src/consensus/params.rs @@ -147,3 +147,7 @@ impl From for &'static Params { impl From<&Network> for &'static Params { fn from(value: &Network) -> Self { value.params() } } + +impl AsRef for Params { + fn as_ref(&self) -> &Params { self } +} diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 6292a822..07c38ff7 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -16,10 +16,8 @@ use units::parse; use crate::blockdata::block::BlockHash; use crate::consensus::encode::{self, Decodable, Encodable}; -#[cfg(doc)] use crate::consensus::Params; use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError}; -use crate::Network; /// Implement traits and methods shared by `Target` and `Work`. macro_rules! do_impl { @@ -234,16 +232,11 @@ impl Target { /// [max]: Target::max /// [target]: crate::blockdata::block::Header::target #[cfg_attr(all(test, mutate), mutate)] - pub fn difficulty(&self, network: Network) -> u128 { + pub fn difficulty(&self, params: impl AsRef) -> u128 { // Panic here may be eaiser to debug than during the actual division. assert_ne!(self.0, U256::ZERO, "divide by zero"); - let max = match network { - Network::Bitcoin => Target::MAX_ATTAINABLE_MAINNET, - Network::Testnet => Target::MAX_ATTAINABLE_TESTNET, - Network::Signet => Target::MAX_ATTAINABLE_SIGNET, - Network::Regtest => Target::MAX_ATTAINABLE_REGTEST, - }; + let max = params.as_ref().pow_limit; let d = max.0 / self.0; d.saturating_to_u128() } From 4121c9a09fba775ae89d1e44082596dd229a6e9d Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 30 Jan 2024 07:54:44 +1100 Subject: [PATCH 6/8] Rename Params::pow_limit to max_attainable_target The maximum "attainable" target is a `rust-bitcoin` thing, Core use max unattainable. Deprecated the `Params::pow_limit` field and add a new field `max_attainable_target`. The `Params` type is `non_exhaustive` so this is not an API breaking change. --- bitcoin/src/consensus/params.rs | 14 +++++++++++++- bitcoin/src/pow.rs | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/consensus/params.rs b/bitcoin/src/consensus/params.rs index 55655384..19522134 100644 --- a/bitcoin/src/consensus/params.rs +++ b/bitcoin/src/consensus/params.rs @@ -7,6 +7,8 @@ //! use crate::network::Network; +#[cfg(doc)] +use crate::pow::CompactTarget; use crate::pow::Target; /// Parameters that influence chain consensus. @@ -30,6 +32,12 @@ 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. + #[deprecated(since = "TBD", note = "field renamed to max_attainable_target")] + pub pow_limit: Target, + /// The maximum **attainable** target value for these params. + /// + /// Not all target values are attainable because consensus code uses the compact format to + /// represent targets (see [`CompactTarget`]). /// /// 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 @@ -37,7 +45,7 @@ pub struct Params { /// 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: Target, + pub max_attainable_target: Target, /// Expected amount of time to mine one block. pub pow_target_spacing: u64, /// Difficulty recalculation interval. @@ -62,6 +70,7 @@ impl Params { rule_change_activation_threshold: 1916, // 95% miner_confirmation_window: 2016, pow_limit: Target::MAX_ATTAINABLE_MAINNET, + max_attainable_target: Target::MAX_ATTAINABLE_MAINNET, pow_target_spacing: 10 * 60, // 10 minutes. pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks. allow_min_difficulty_blocks: false, @@ -78,6 +87,7 @@ impl Params { rule_change_activation_threshold: 1512, // 75% miner_confirmation_window: 2016, pow_limit: Target::MAX_ATTAINABLE_TESTNET, + max_attainable_target: Target::MAX_ATTAINABLE_TESTNET, pow_target_spacing: 10 * 60, // 10 minutes. pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks. allow_min_difficulty_blocks: true, @@ -94,6 +104,7 @@ impl Params { rule_change_activation_threshold: 1916, // 95% miner_confirmation_window: 2016, pow_limit: Target::MAX_ATTAINABLE_SIGNET, + max_attainable_target: Target::MAX_ATTAINABLE_SIGNET, pow_target_spacing: 10 * 60, // 10 minutes. pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks. allow_min_difficulty_blocks: false, @@ -110,6 +121,7 @@ impl Params { rule_change_activation_threshold: 108, // 75% miner_confirmation_window: 144, pow_limit: Target::MAX_ATTAINABLE_REGTEST, + max_attainable_target: Target::MAX_ATTAINABLE_REGTEST, pow_target_spacing: 10 * 60, // 10 minutes. pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks. allow_min_difficulty_blocks: true, diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 07c38ff7..eec84379 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -236,7 +236,7 @@ impl Target { // Panic here may be eaiser to debug than during the actual division. assert_ne!(self.0, U256::ZERO, "divide by zero"); - let max = params.as_ref().pow_limit; + let max = params.as_ref().max_attainable_target; let d = max.0 / self.0; d.saturating_to_u128() } From 6e47d57744a4ddc76b13b12586ded228525b4707 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 30 Jan 2024 07:57:30 +1100 Subject: [PATCH 7/8] Rename difficulty transition threshold functions These two functions calculate the min/max threshold transition which is a _target_ not a "difficulty" number. Using "difficulty" in the function name is unnecessarily confusing. Rename and deprecate the functions. --- bitcoin/src/pow.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index eec84379..3e28029a 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -253,19 +253,30 @@ impl Target { #[cfg_attr(all(test, mutate), mutate)] pub fn difficulty_float(&self) -> f64 { TARGET_MAX_F64 / self.0.to_f64() } + /// Computes the minimum valid [`Target`] threshold allowed for a block in which a difficulty + /// adjustment occurs. + #[deprecated(since = "TBD", note = "use min_transition_threshold instead")] + pub fn min_difficulty_transition_threshold(&self) -> Self { self.min_transition_threshold() } + + /// Computes the maximum valid [`Target`] threshold allowed for a block in which a difficulty + /// adjustment occurs. + #[deprecated(since = "TBD", note = "use max_transition_threshold instead")] + pub fn max_difficulty_transition_threshold(&self) -> Self { self.max_transition_threshold() } + /// Computes the minimum valid [`Target`] threshold allowed for a block in which a difficulty /// adjustment occurs. /// /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty /// adjustment period. - pub fn min_difficulty_transition_threshold(&self) -> Self { Self(self.0 >> 2) } + pub fn min_transition_threshold(&self) -> Self { Self(self.0 >> 2) } /// Computes the maximum valid [`Target`] threshold allowed for a block in which a difficulty /// adjustment occurs. /// /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty /// adjustment period. - pub fn max_difficulty_transition_threshold(&self) -> Self { Self(self.0 << 2) } + pub fn max_transition_threshold(&self) -> Self { Self(self.0 << 2) } + } do_impl!(Target); From fd6fedc3ad07de9fc8e13eb8dd9beaa8e6dc8785 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 30 Jan 2024 08:20:33 +1100 Subject: [PATCH 8/8] Improve API for max target threshold calculation The maximum target threshold has a network dependant upper bound. Currently we are not checking this bound. One complication is that there is currently heated open debate around the `Network` type. We can bypass the `Network` issue by using `AsRef` instead. Add a function that does the checks based on the `Params` type as well as an unchecked version. --- bitcoin/src/pow.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 3e28029a..4fac62f3 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -6,6 +6,7 @@ //! functions here are designed to be fast, by that we mean it is safe to use them to check headers. //! +use core::cmp; use core::fmt::{self, LowerHex, UpperHex}; use core::ops::{Add, Div, Mul, Not, Rem, Shl, Shr, Sub}; @@ -261,13 +262,19 @@ impl Target { /// Computes the maximum valid [`Target`] threshold allowed for a block in which a difficulty /// adjustment occurs. #[deprecated(since = "TBD", note = "use max_transition_threshold instead")] - pub fn max_difficulty_transition_threshold(&self) -> Self { self.max_transition_threshold() } + pub fn max_difficulty_transition_threshold(&self) -> Self { + self.max_transition_threshold_unchecked() + } /// Computes the minimum valid [`Target`] threshold allowed for a block in which a difficulty /// adjustment occurs. /// /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty /// adjustment period. + /// + /// # Returns + /// + /// In line with Bitcoin Core this function may return a target value of zero. pub fn min_transition_threshold(&self) -> Self { Self(self.0 >> 2) } /// Computes the maximum valid [`Target`] threshold allowed for a block in which a difficulty @@ -275,8 +282,27 @@ impl Target { /// /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty /// adjustment period. - pub fn max_transition_threshold(&self) -> Self { Self(self.0 << 2) } + /// + /// We also check that the calculated target is not greater than the maximum allowed target, + /// this value is network specific - hence the `params` parameter. + pub fn max_transition_threshold(&self, params: impl AsRef) -> Self { + let max_attainable = params.as_ref().max_attainable_target; + cmp::min(self.max_transition_threshold_unchecked(), max_attainable) + } + /// Computes the maximum valid [`Target`] threshold allowed for a block in which a difficulty + /// adjustment occurs. + /// + /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty + /// adjustment period. + /// + /// # Returns + /// + /// This function may return a value greater than the maximum allowed target for this network. + /// + /// The return value should be checked against [`Params::max_attainable_target`] or use one of + /// the `Target::MAX_ATTAINABLE_FOO` constants. + pub fn max_transition_threshold_unchecked(&self) -> Self { Self(self.0 << 2) } } do_impl!(Target);