From 3d85ee3a02afceca087c024076432e1dc10d9cca Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 8 Aug 2024 10:57:38 +1000 Subject: [PATCH 1/8] primitives: Fix alloc feature We have an `alloc` feature but we are unconditionally using `extern crate alloc`, this is broken - clearly we need to add a `no-std` build for `primitives` in CI. Feature gate the `alloc` crate. While we are at it just pull types in from `alloc` in the `prelude` - I have no idea why we do not do this in `bitcoin`? --- primitives/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 598783835..65074412e 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -18,6 +18,7 @@ #![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; #[cfg(feature = "std")] @@ -44,9 +45,6 @@ pub use self::sequence::Sequence; #[rustfmt::skip] #[allow(unused_imports)] mod prelude { - #[cfg(all(not(feature = "std"), not(test)))] + #[cfg(feature = "alloc")] pub use alloc::string::ToString; - - #[cfg(any(feature = "std", test))] - pub use std::string::ToString; } From 244d7dbe6cc5fe7fc32e61a7142530c668ba5bd9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Jul 2024 09:00:34 +1000 Subject: [PATCH 2/8] Remove generic test impl In preparation for moving the `CompactTarget` to `primitives` remove the generic `Into` impl and explicitly implement for just the `From` impls that the `pow` unit tests use. Test code only. --- bitcoin/src/pow.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index abf449588..6c5986c3f 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -1099,8 +1099,12 @@ impl kani::Arbitrary for U256 { mod tests { use super::*; - impl> From for Target { - fn from(x: T) -> Self { Self(U256::from(x)) } + impl From for Target { + fn from(x: u64) -> Self { Self(U256::from(x)) } + } + + impl From for Target { + fn from(x: u32) -> Self { Self(U256::from(x)) } } impl> From for Work { From 22d5646f7b6206987f2410b090f2b8a078d13d73 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Jul 2024 08:53:19 +1000 Subject: [PATCH 3/8] Stop using CompactTarget inner field In preparation for moving the `CompactTarget` type to `primitives` stop using the inner field in code that will stay behind in the `bitcoin::pow` module. --- bitcoin/src/pow.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 6c5986c3f..a9c403220 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -164,7 +164,7 @@ impl Target { /// /// ref: pub fn from_compact(c: CompactTarget) -> Target { - let bits = c.0; + let bits = c.to_consensus(); // This is a floating-point "compact" encoding originally used by // OpenSSL, which satoshi put into consensus code, so we're stuck // with it. The exponent needs to have 3 subtracted from it, hence @@ -204,7 +204,7 @@ impl Target { size += 1; } - CompactTarget(compact | (size << 24)) + CompactTarget::from_consensus(compact | (size << 24)) } /// Returns true if block hash is less than or equal to this [`Target`]. @@ -450,14 +450,14 @@ impl From for Target { impl Encodable for CompactTarget { #[inline] fn consensus_encode(&self, w: &mut W) -> Result { - self.0.consensus_encode(w) + self.to_consensus().consensus_encode(w) } } impl Decodable for CompactTarget { #[inline] fn consensus_decode(r: &mut R) -> Result { - u32::consensus_decode(r).map(CompactTarget) + u32::consensus_decode(r).map(CompactTarget::from_consensus) } } @@ -1725,25 +1725,25 @@ mod tests { #[test] fn compact_target_from_hex_lower() { let target = CompactTarget::from_hex("0x010034ab").unwrap(); - assert_eq!(target, CompactTarget(0x010034ab)); + assert_eq!(target, CompactTarget::from_consensus(0x010034ab)); } #[test] fn compact_target_from_hex_upper() { let target = CompactTarget::from_hex("0X010034AB").unwrap(); - assert_eq!(target, CompactTarget(0x010034ab)); + assert_eq!(target, CompactTarget::from_consensus(0x010034ab)); } #[test] fn compact_target_from_unprefixed_hex_lower() { let target = CompactTarget::from_unprefixed_hex("010034ab").unwrap(); - assert_eq!(target, CompactTarget(0x010034ab)); + assert_eq!(target, CompactTarget::from_consensus(0x010034ab)); } #[test] fn compact_target_from_unprefixed_hex_upper() { let target = CompactTarget::from_unprefixed_hex("010034AB").unwrap(); - assert_eq!(target, CompactTarget(0x010034ab)); + assert_eq!(target, CompactTarget::from_consensus(0x010034ab)); } #[test] @@ -1755,8 +1755,8 @@ mod tests { #[test] fn compact_target_lower_hex_and_upper_hex() { - assert_eq!(format!("{:08x}", CompactTarget(0x01D0F456)), "01d0f456"); - assert_eq!(format!("{:08X}", CompactTarget(0x01d0f456)), "01D0F456"); + assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0F456)), "01d0f456"); + assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0f456)), "01D0F456"); } #[test] From 578143c09ec84dfe127d5df1f1326e61be5d1646 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 12 Aug 2024 20:12:01 +1000 Subject: [PATCH 4/8] Separate CompactTarget impl blocks In preparation for adding an `CompactTargetExt` trait move the primitives methods to a separate impl block. Refactor only, no logic changes. --- bitcoin/src/pow.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index a9c403220..bbd812faa 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -347,6 +347,14 @@ do_impl!(Target); #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct CompactTarget(u32); +impl CompactTarget { + /// Creates a [`CompactTarget`] from a consensus encoded `u32`. + pub fn from_consensus(bits: u32) -> Self { Self(bits) } + + /// Returns the consensus encoded `u32` representation of this [`CompactTarget`]. + pub fn to_consensus(self) -> u32 { self.0 } +} + impl CompactTarget { /// Creates a `CompactTarget` from a prefixed hex string. pub fn from_hex(s: &str) -> Result { @@ -435,12 +443,6 @@ impl CompactTarget { let bits = current.bits; CompactTarget::from_next_work_required(bits, timespan.into(), params) } - - /// Creates a [`CompactTarget`] from a consensus encoded `u32`. - pub fn from_consensus(bits: u32) -> Self { Self(bits) } - - /// Returns the consensus encoded `u32` representation of this [`CompactTarget`]. - pub fn to_consensus(self) -> u32 { self.0 } } impl From for Target { From 9c4a62965960ae02efb63be86838de0c72d1b559 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 12 Aug 2024 20:16:12 +1000 Subject: [PATCH 5/8] Wrap CompactTarget impl block in temporary module `rustfmt` is unable to format macro calls so instead we wrap the impl blocks in a module to enable formatting in the next commit. --- bitcoin/src/pow.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index bbd812faa..32a47a9b3 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -355,6 +355,9 @@ impl CompactTarget { pub fn to_consensus(self) -> u32 { self.0 } } +mod tmp { + use super::*; + impl CompactTarget { /// Creates a `CompactTarget` from a prefixed hex string. pub fn from_hex(s: &str) -> Result { @@ -444,6 +447,7 @@ impl CompactTarget { CompactTarget::from_next_work_required(bits, timespan.into(), params) } } +} impl From for Target { fn from(c: CompactTarget) -> Self { Target::from_compact(c) } From 100ce03643efc51f83341825b1b726c8b3084aa3 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 12 Aug 2024 20:17:43 +1000 Subject: [PATCH 6/8] Run cargo +nightly fmt No manual changes. --- bitcoin/src/pow.rs | 172 ++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 32a47a9b3..2afc5b8c2 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -358,96 +358,96 @@ impl CompactTarget { mod tmp { use super::*; -impl CompactTarget { - /// Creates a `CompactTarget` from a prefixed hex string. - pub fn from_hex(s: &str) -> Result { - let target = parse::hex_u32_prefixed(s)?; - Ok(Self::from_consensus(target)) - } - - /// Creates a `CompactTarget` from an unprefixed hex string. - pub fn from_unprefixed_hex(s: &str) -> Result { - let target = parse::hex_u32_unprefixed(s)?; - Ok(Self::from_consensus(target)) - } - - /// Computes the [`CompactTarget`] from a difficulty adjustment. - /// - /// ref: - /// - /// Given the previous Target, represented as a [`CompactTarget`], the difficulty is adjusted - /// by taking the timespan between them, and multipling the current [`CompactTarget`] by a factor - /// of the net timespan and expected timespan. The [`CompactTarget`] may not adjust by more than - /// a factor of 4, or adjust beyond the maximum threshold for the network. - /// - /// # Note - /// - /// Under the consensus rules, the difference in the number of blocks between the headers does - /// not equate to the `difficulty_adjustment_interval` of [`Params`]. This is due to an off-by-one - /// error, and, the expected number of blocks in between headers is `difficulty_adjustment_interval - 1` - /// when calculating the difficulty adjustment. - /// - /// Take the example of the first difficulty adjustment. Block 2016 introduces a new [`CompactTarget`], - /// which takes the net timespan between Block 2015 and Block 0, and recomputes the difficulty. - /// - /// # Returns - /// - /// The expected [`CompactTarget`] recalculation. - pub fn from_next_work_required( - last: CompactTarget, - timespan: u64, - params: impl AsRef, - ) -> CompactTarget { - let params = params.as_ref(); - if params.no_pow_retargeting { - return last; + impl CompactTarget { + /// Creates a `CompactTarget` from a prefixed hex string. + pub fn from_hex(s: &str) -> Result { + let target = parse::hex_u32_prefixed(s)?; + Ok(Self::from_consensus(target)) } - // Comments relate to the `pow.cpp` file from Core. - // ref: - let min_timespan = params.pow_target_timespan >> 2; // Lines 56/57 - let max_timespan = params.pow_target_timespan << 2; // Lines 58/59 - let actual_timespan = timespan.clamp(min_timespan, max_timespan); - let prev_target: Target = last.into(); - let maximum_retarget = prev_target.max_transition_threshold(params); // bnPowLimit - let retarget = prev_target.0; // bnNew - let retarget = retarget.mul(actual_timespan.into()); - let retarget = retarget.div(params.pow_target_timespan.into()); - let retarget = Target(retarget); - if retarget.ge(&maximum_retarget) { - return maximum_retarget.to_compact_lossy(); - } - retarget.to_compact_lossy() - } - /// Computes the [`CompactTarget`] from a difficulty adjustment, - /// assuming these are the relevant block headers. - /// - /// Given two headers, representing the start and end of a difficulty adjustment epoch, - /// compute the [`CompactTarget`] based on the net time between them and the current - /// [`CompactTarget`]. - /// - /// # Note - /// - /// See [`CompactTarget::from_next_work_required`] - /// - /// For example, to successfully compute the first difficulty adjustment on the Bitcoin network, - /// one would pass the header for Block 2015 as `current` and the header for Block 0 as - /// `last_epoch_boundary`. - /// - /// # Returns - /// - /// The expected [`CompactTarget`] recalculation. - pub fn from_header_difficulty_adjustment( - last_epoch_boundary: Header, - current: Header, - params: impl AsRef, - ) -> CompactTarget { - let timespan = current.time - last_epoch_boundary.time; - let bits = current.bits; - CompactTarget::from_next_work_required(bits, timespan.into(), params) + /// Creates a `CompactTarget` from an unprefixed hex string. + pub fn from_unprefixed_hex(s: &str) -> Result { + let target = parse::hex_u32_unprefixed(s)?; + Ok(Self::from_consensus(target)) + } + + /// Computes the [`CompactTarget`] from a difficulty adjustment. + /// + /// ref: + /// + /// Given the previous Target, represented as a [`CompactTarget`], the difficulty is adjusted + /// by taking the timespan between them, and multipling the current [`CompactTarget`] by a factor + /// of the net timespan and expected timespan. The [`CompactTarget`] may not adjust by more than + /// a factor of 4, or adjust beyond the maximum threshold for the network. + /// + /// # Note + /// + /// Under the consensus rules, the difference in the number of blocks between the headers does + /// not equate to the `difficulty_adjustment_interval` of [`Params`]. This is due to an off-by-one + /// error, and, the expected number of blocks in between headers is `difficulty_adjustment_interval - 1` + /// when calculating the difficulty adjustment. + /// + /// Take the example of the first difficulty adjustment. Block 2016 introduces a new [`CompactTarget`], + /// which takes the net timespan between Block 2015 and Block 0, and recomputes the difficulty. + /// + /// # Returns + /// + /// The expected [`CompactTarget`] recalculation. + pub fn from_next_work_required( + last: CompactTarget, + timespan: u64, + params: impl AsRef, + ) -> CompactTarget { + let params = params.as_ref(); + if params.no_pow_retargeting { + return last; + } + // Comments relate to the `pow.cpp` file from Core. + // ref: + let min_timespan = params.pow_target_timespan >> 2; // Lines 56/57 + let max_timespan = params.pow_target_timespan << 2; // Lines 58/59 + let actual_timespan = timespan.clamp(min_timespan, max_timespan); + let prev_target: Target = last.into(); + let maximum_retarget = prev_target.max_transition_threshold(params); // bnPowLimit + let retarget = prev_target.0; // bnNew + let retarget = retarget.mul(actual_timespan.into()); + let retarget = retarget.div(params.pow_target_timespan.into()); + let retarget = Target(retarget); + if retarget.ge(&maximum_retarget) { + return maximum_retarget.to_compact_lossy(); + } + retarget.to_compact_lossy() + } + + /// Computes the [`CompactTarget`] from a difficulty adjustment, + /// assuming these are the relevant block headers. + /// + /// Given two headers, representing the start and end of a difficulty adjustment epoch, + /// compute the [`CompactTarget`] based on the net time between them and the current + /// [`CompactTarget`]. + /// + /// # Note + /// + /// See [`CompactTarget::from_next_work_required`] + /// + /// For example, to successfully compute the first difficulty adjustment on the Bitcoin network, + /// one would pass the header for Block 2015 as `current` and the header for Block 0 as + /// `last_epoch_boundary`. + /// + /// # Returns + /// + /// The expected [`CompactTarget`] recalculation. + pub fn from_header_difficulty_adjustment( + last_epoch_boundary: Header, + current: Header, + params: impl AsRef, + ) -> CompactTarget { + let timespan = current.time - last_epoch_boundary.time; + let bits = current.bits; + CompactTarget::from_next_work_required(bits, timespan.into(), params) + } } } -} impl From for Target { fn from(c: CompactTarget) -> Self { Target::from_compact(c) } From a00bd7cc4dd891858c05a688e72b6463b84f7d22 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Jul 2024 08:42:28 +1000 Subject: [PATCH 7/8] Introduce CompactTargetExt trait In preparation for moving the `CompactTarget` type to `primitives` introduce an extension trait for code that will be left behind in `bitcoin`. --- bitcoin/src/pow.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 2afc5b8c2..aa986a24b 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -15,6 +15,7 @@ use units::parse::{self, ParseIntError, PrefixedHexError, UnprefixedHexError}; use crate::block::{BlockHash, Header}; use crate::consensus::encode::{self, Decodable, Encodable}; +use crate::internal_macros::define_extension_trait; use crate::network::Params; /// Implement traits and methods shared by `Target` and `Work`. @@ -355,18 +356,17 @@ impl CompactTarget { pub fn to_consensus(self) -> u32 { self.0 } } -mod tmp { - use super::*; - - impl CompactTarget { +define_extension_trait! { + /// Extension functionality for the [`CompactTarget`] type. + pub trait CompactTargetExt impl for CompactTarget { /// Creates a `CompactTarget` from a prefixed hex string. - pub fn from_hex(s: &str) -> Result { + fn from_hex(s: &str) -> Result { let target = parse::hex_u32_prefixed(s)?; Ok(Self::from_consensus(target)) } /// Creates a `CompactTarget` from an unprefixed hex string. - pub fn from_unprefixed_hex(s: &str) -> Result { + fn from_unprefixed_hex(s: &str) -> Result { let target = parse::hex_u32_unprefixed(s)?; Ok(Self::from_consensus(target)) } @@ -393,7 +393,7 @@ mod tmp { /// # Returns /// /// The expected [`CompactTarget`] recalculation. - pub fn from_next_work_required( + fn from_next_work_required( last: CompactTarget, timespan: u64, params: impl AsRef, @@ -437,7 +437,7 @@ mod tmp { /// # Returns /// /// The expected [`CompactTarget`] recalculation. - pub fn from_header_difficulty_adjustment( + fn from_header_difficulty_adjustment( last_epoch_boundary: Header, current: Header, params: impl AsRef, From 2ec901fd639f37aa4c25e1e630ad1a7051d7cc8f Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Jul 2024 09:02:30 +1000 Subject: [PATCH 8/8] Move the CompactTarget type to primitives Potentially the whole `pow` module will move to `primitives` but this is not possible easily right now. However, we would like to be able to move the `BlockHash` and `block::Header` types over to `primitives` and doing so requires the `CompactTarget` to be there. Move the `CompactTarget` type to `primitives` and re-export it from the `primitives` crate root. Note also, we re-export the type publicly from `bitcoin::pow`. --- bitcoin/src/pow.rs | 40 ++++------------------------------------ primitives/src/lib.rs | 6 +++++- primitives/src/pow.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 primitives/src/pow.rs diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index aa986a24b..7353e6db1 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -18,6 +18,10 @@ use crate::consensus::encode::{self, Decodable, Encodable}; use crate::internal_macros::define_extension_trait; use crate::network::Params; +#[rustfmt::skip] // Keep public re-exports separate. +#[doc(inline)] +pub use primitives::CompactTarget; + /// Implement traits and methods shared by `Target` and `Work`. macro_rules! do_impl { ($ty:ident) => { @@ -330,32 +334,6 @@ impl Target { } do_impl!(Target); -/// Encoding of 256-bit target as 32-bit float. -/// -/// This is used to encode a target into the block header. Satoshi made this part of consensus code -/// in the original version of Bitcoin, likely copying an idea from OpenSSL. -/// -/// OpenSSL's bignum (BN) type has an encoding, which is even called "compact" as in bitcoin, which -/// is exactly this format. -/// -/// # Note on order/equality -/// -/// Usage of the ordering and equality traits for this type may be surprising. Converting between -/// `CompactTarget` and `Target` is lossy *in both directions* (there are multiple `CompactTarget` -/// values that map to the same `Target` value). Ordering and equality for this type are defined in -/// terms of the underlying `u32`. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct CompactTarget(u32); - -impl CompactTarget { - /// Creates a [`CompactTarget`] from a consensus encoded `u32`. - pub fn from_consensus(bits: u32) -> Self { Self(bits) } - - /// Returns the consensus encoded `u32` representation of this [`CompactTarget`]. - pub fn to_consensus(self) -> u32 { self.0 } -} - define_extension_trait! { /// Extension functionality for the [`CompactTarget`] type. pub trait CompactTargetExt impl for CompactTarget { @@ -467,16 +445,6 @@ impl Decodable for CompactTarget { } } -impl fmt::LowerHex for CompactTarget { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } -} - -impl fmt::UpperHex for CompactTarget { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) } -} - /// Big-endian 256 bit integer type. // (high, low): u.0 contains the high bits, u.1 contains the low bits. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 65074412e..78838eaf5 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -31,6 +31,7 @@ extern crate serde; #[cfg(feature = "alloc")] pub mod locktime; pub mod opcodes; +pub mod pow; pub mod sequence; #[doc(inline)] @@ -40,7 +41,10 @@ pub use units::*; #[cfg(feature = "alloc")] pub use self::locktime::{absolute, relative}; #[doc(inline)] -pub use self::sequence::Sequence; +pub use self::{ + pow::CompactTarget, + sequence::Sequence, +}; #[rustfmt::skip] #[allow(unused_imports)] diff --git a/primitives/src/pow.rs b/primitives/src/pow.rs new file mode 100644 index 000000000..b1e8aaa7f --- /dev/null +++ b/primitives/src/pow.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Proof-of-work related integer types. + +use core::fmt; + +/// Encoding of 256-bit target as 32-bit float. +/// +/// This is used to encode a target into the block header. Satoshi made this part of consensus code +/// in the original version of Bitcoin, likely copying an idea from OpenSSL. +/// +/// OpenSSL's bignum (BN) type has an encoding, which is even called "compact" as in bitcoin, which +/// is exactly this format. +/// +/// # Note on order/equality +/// +/// Usage of the ordering and equality traits for this type may be surprising. Converting between +/// `CompactTarget` and `Target` is lossy *in both directions* (there are multiple `CompactTarget` +/// values that map to the same `Target` value). Ordering and equality for this type are defined in +/// terms of the underlying `u32`. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CompactTarget(u32); + +impl CompactTarget { + /// Creates a [`CompactTarget`] from a consensus encoded `u32`. + pub fn from_consensus(bits: u32) -> Self { Self(bits) } + + /// Returns the consensus encoded `u32` representation of this [`CompactTarget`]. + pub fn to_consensus(self) -> u32 { self.0 } +} + +impl fmt::LowerHex for CompactTarget { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } +} + +impl fmt::UpperHex for CompactTarget { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) } +}