Move CompactTarget from primitives to units
What changed, and why it matters
This commit is a routine code reorganization: it moves the CompactTarget type (and its proof-of-work module) from one internal crate (`primitives`) to another (`units`), while keeping the same public API available through re-exports. There is no security bug being fixed and no behavior change visible to users.
No security action needed. Treat as normal refactoring; verify downstream consumers still compile due to the preserved re-exports.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates CompactTarget, CompactTargetEncoder, CompactTargetDecoder, and CompactTargetDecoderError from primitives/src/pow.rs to units/src/pow.rs. Public exports are preserved: primitives re-exports from units::pow, and bitcoin re-exports CompactTarget from units::pow instead of primitives::pow. The implementation is essentially identical, with encoding/serde/arbitrary code now gated by the corresponding units crate features (encoding, serde, arbitrary). API tests in units/tests/api.rs are updated to include the moved type. No functional or security-relevant change is present.
Changed components
primitives/src/pow.rs (removed)units/src/pow.rs (added)bitcoin/src/lib.rs (re-export path)primitives/src/lib.rs (re-export path)units/src/lib.rs (module and re-export)units/tests/api.rs (API test coverage)Inspect captured patch +283 / −247
diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml
index e6cfccb1..d65eabc2 100644
--- a/.cargo/mutants.toml
+++ b/.cargo/mutants.toml
@@ -23,6 +23,7 @@ exclude_re = [
"units/.* dec_width", # Replacing num /= 10 with num %=10 in a loop causes a timeout due to infinite loop
# src/locktime/relative.rs
"units/.* LockTime::to_consensus_u32", # Mutant from replacing | with ^, this returns the same value since the XOR is taken against the u16 with an all-zero bitmask
+ "units/.* CompactTarget::to_hex", # Deprecated
"units/.* FeeRate::fee_vb", # Deprecated
"units/.* FeeRate::fee_wu", # Deprecated
"units/.* SignedAmount::checked_abs", # Deprecated
@@ -43,7 +44,6 @@ exclude_re = [
"primitives/.* decode_cursor", # Mutating operations in decode_cursor can result in an infinite loop
"primitives/.* fmt_debug", # Mutants from formatting/display changes
"primitives/.* fmt_debug_pretty", # Mutants from formatting/display changes
- "primitives/.* CompactTarget::to_hex", # Deprecated
"primitives/.* Script::to_hex", # Deprecated
"primitives/.* Script<T>::to_hex", # Deprecated
"primitives/.* ScriptBuf::to_hex", # Deprecated
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 11478557..fda5ddb3 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -146,7 +146,6 @@ pub use primitives::{
Validation as BlockValidation, Version as BlockVersion, WitnessCommitment,
},
merkle_tree::{TxMerkleNode, WitnessMerkleNode},
- pow::CompactTarget, // No `pow` module outside of `primitives`.
script::{
RedeemScript, RedeemScriptBuf, RedeemScriptTag, ScriptHashableTag, ScriptPubKey,
ScriptPubKeyBuf, ScriptPubKeyTag, ScriptSig, ScriptSigBuf, ScriptSigTag, Tag, TapScript,
@@ -163,6 +162,7 @@ pub use units::{
block::{BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval},
fee_rate::FeeRate,
parse_int,
+ pow::CompactTarget,
result::{self, NumOpResult},
sequence::{self, Sequence},
time::{self, BlockTime, BlockTimeDecoder, BlockTimeDecoderError},
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index 96aba956..8669c35d 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -2011,12 +2011,6 @@ mod tests {
assert!(result.is_err());
}
- #[test]
- fn compact_target_lower_hex_and_upper_hex() {
- assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0F456)), "01d0f456");
- assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0f456)), "01D0F456");
- }
-
#[test]
fn compact_target_from_upwards_difficulty_adjustment() {
let params = Params::new(crate::Network::Signet);
diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs
index d59ce80b..2dca4630 100644
--- a/primitives/src/lib.rs
+++ b/primitives/src/lib.rs
@@ -46,7 +46,6 @@ mod opcodes;
pub mod block;
pub mod merkle_tree;
-pub mod pow;
#[cfg(feature = "alloc")]
pub mod script;
pub mod transaction;
@@ -60,6 +59,7 @@ pub use units::{
fee_rate::{self, FeeRate},
locktime::{self, absolute, relative},
parse_int,
+ pow::{self, CompactTarget},
result::{self, NumOpResult},
sequence::{self, Sequence},
time::{self, BlockTime},
@@ -87,7 +87,6 @@ pub use self::{
pub use self::{
block::{BlockHash, Header as BlockHeader, Version as BlockVersion, WitnessCommitment},
merkle_tree::{TxMerkleNode, WitnessMerkleNode},
- pow::CompactTarget,
transaction::{Ntxid, OutPoint, Txid, Version as TransactionVersion, Wtxid},
};
diff --git a/primitives/src/pow.rs b/primitives/src/pow.rs
deleted file mode 100644
index 41e552fb..00000000
--- a/primitives/src/pow.rs
+++ /dev/null
@@ -1,222 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Proof-of-work related integer types.
-
-use core::convert::Infallible;
-use core::fmt;
-
-#[cfg(feature = "arbitrary")]
-use arbitrary::{Arbitrary, Unstructured};
-use internals::write_err;
-
-/// 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, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub struct CompactTarget(u32);
-
-impl CompactTarget {
- /// Constructs a new [`CompactTarget`] from a consensus encoded `u32`.
- #[inline]
- pub fn from_consensus(bits: u32) -> Self { Self(bits) }
-
- /// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
- #[inline]
- pub const fn to_consensus(self) -> u32 { self.0 }
-
- /// Gets the hex representation of this [`CompactTarget`].
- #[cfg(feature = "alloc")]
- #[inline]
- #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
- pub fn to_hex(self) -> alloc::string::String { alloc::format!("{:x}", self) }
-}
-
-impl fmt::Display for CompactTarget {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
-}
-
-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) }
-}
-
-impl fmt::Octal for CompactTarget {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Octal::fmt(&self.0, f) }
-}
-
-impl fmt::Binary for CompactTarget {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Binary::fmt(&self.0, f) }
-}
-
-encoding::encoder_newtype_exact! {
- /// The encoder for the [`CompactTarget`] type.
- pub struct CompactTargetEncoder<'e>(encoding::ArrayEncoder<4>);
-}
-
-impl encoding::Encodable for CompactTarget {
- type Encoder<'e> = CompactTargetEncoder<'e>;
- fn encoder(&self) -> Self::Encoder<'_> {
- CompactTargetEncoder::new(encoding::ArrayEncoder::without_length_prefix(
- self.to_consensus().to_le_bytes(),
- ))
- }
-}
-
-/// The decoder for the [`CompactTarget`] type.
-pub struct CompactTargetDecoder(encoding::ArrayDecoder<4>);
-
-impl CompactTargetDecoder {
- /// Constructs a new [`CompactTarget`] decoder.
- pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
-}
-
-impl Default for CompactTargetDecoder {
- fn default() -> Self { Self::new() }
-}
-
-impl encoding::Decoder for CompactTargetDecoder {
- type Output = CompactTarget;
- type Error = CompactTargetDecoderError;
-
- #[inline]
- fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
- self.0.push_bytes(bytes).map_err(CompactTargetDecoderError)
- }
-
- #[inline]
- fn end(self) -> Result<Self::Output, Self::Error> {
- let n = u32::from_le_bytes(self.0.end().map_err(CompactTargetDecoderError)?);
- Ok(CompactTarget::from_consensus(n))
- }
-
- #[inline]
- fn read_limit(&self) -> usize { self.0.read_limit() }
-}
-
-impl encoding::Decodable for CompactTarget {
- type Decoder = CompactTargetDecoder;
- fn decoder() -> Self::Decoder { CompactTargetDecoder(encoding::ArrayDecoder::<4>::new()) }
-}
-
-/// An error consensus decoding an `CompactTarget`.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CompactTargetDecoderError(encoding::UnexpectedEofError);
-
-impl From<Infallible> for CompactTargetDecoderError {
- fn from(never: Infallible) -> Self { match never {} }
-}
-
-impl fmt::Display for CompactTargetDecoderError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write_err!(f, "sequence decoder error"; self.0)
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for CompactTargetDecoderError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
-}
-
-#[cfg(feature = "arbitrary")]
-impl<'a> Arbitrary<'a> for CompactTarget {
- fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
- Ok(Self::from_consensus(u.arbitrary()?))
- }
-}
-
-#[cfg(test)]
-mod tests {
- #[cfg(feature = "alloc")]
- use alloc::format;
- #[cfg(feature = "alloc")]
- use alloc::string::ToString;
- #[cfg(feature = "std")]
- use std::error::Error as _;
-
- use encoding::Decoder as _;
-
- use super::*;
-
- #[test]
- fn compact_target_decoder_read_limit() {
- // read_limit is one u32 = 4 bytes for empty decoder
- assert_eq!(CompactTargetDecoder::default().read_limit(), 4);
- assert_eq!(<CompactTarget as encoding::Decodable>::decoder().read_limit(), 4);
- }
-
- #[test]
- fn compact_target_decoder_round_trip() {
- let bits: u32 = 0x1d00_ffff;
- let compact_target =
- encoding::decode_from_slice::<CompactTarget>(&bits.to_le_bytes()).unwrap();
- assert_eq!(compact_target.to_consensus(), bits);
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- #[allow(deprecated)]
- fn compact_target_to_hex() {
- let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
- assert_eq!(compact_target.to_hex(), "1d00ffff");
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn compact_target_decoder_error_display_and_source() {
- let mut slice = [0u8; 3].as_slice();
- let mut decoder = CompactTargetDecoder::new();
-
- assert!(decoder.push_bytes(&mut slice).unwrap());
-
- let err = decoder.end().unwrap_err();
- assert!(!err.to_string().is_empty());
- #[cfg(feature = "std")]
- assert!(err.source().is_some());
- }
-
- #[test]
- fn compact_target_ordering() {
- let lower = CompactTarget::from_consensus(0x1d00_fffe);
- let lower_copy = CompactTarget::from_consensus(0x1d00_fffe);
- let higher = CompactTarget::from_consensus(0x1d00_ffff);
-
- assert!(lower < higher);
- assert!(lower == lower_copy);
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn compact_target_formatting() {
- let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
- assert_eq!(format!("{}", compact_target), "486604799");
- assert_eq!(format!("{:x}", compact_target), "1d00ffff");
- assert_eq!(format!("{:#x}", compact_target), "0x1d00ffff");
- assert_eq!(format!("{:X}", compact_target), "1D00FFFF");
- assert_eq!(format!("{:#X}", compact_target), "0x1D00FFFF");
- assert_eq!(format!("{:o}", compact_target), "3500177777");
- assert_eq!(format!("{:#o}", compact_target), "0o3500177777");
- assert_eq!(format!("{:b}", compact_target), "11101000000001111111111111111");
- assert_eq!(format!("{:#b}", compact_target), "0b11101000000001111111111111111");
- assert_eq!(compact_target.to_consensus(), 0x1d00_ffff);
- }
-}
diff --git a/units/src/lib.rs b/units/src/lib.rs
index 960c8e0c..36af3f4e 100644
--- a/units/src/lib.rs
+++ b/units/src/lib.rs
@@ -50,6 +50,7 @@ pub mod block;
pub mod fee_rate;
pub mod locktime;
pub mod parse_int;
+pub mod pow;
pub mod result;
pub mod sequence;
pub mod time;
@@ -62,6 +63,7 @@ pub use self::{
block::{BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval},
fee_rate::FeeRate,
locktime::{absolute, relative},
+ pow::CompactTarget,
result::NumOpResult,
sequence::Sequence,
time::BlockTime,
diff --git a/units/src/pow.rs b/units/src/pow.rs
new file mode 100644
index 00000000..f8e73d09
--- /dev/null
+++ b/units/src/pow.rs
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Proof-of-work related integer types.
+
+#[cfg(feature = "encoding")]
+use core::convert::Infallible;
+use core::fmt;
+
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+#[cfg(feature = "encoding")]
+use internals::write_err;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+/// 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, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub struct CompactTarget(u32);
+
+impl CompactTarget {
+ /// Constructs a new [`CompactTarget`] from a consensus encoded `u32`.
+ #[inline]
+ pub fn from_consensus(bits: u32) -> Self { Self(bits) }
+
+ /// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
+ #[inline]
+ pub const fn to_consensus(self) -> u32 { self.0 }
+
+ /// Gets the hex representation of this [`CompactTarget`].
+ #[cfg(feature = "alloc")]
+ #[inline]
+ #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
+ pub fn to_hex(self) -> alloc::string::String { alloc::format!("{:x}", self) }
+}
+
+impl fmt::Display for CompactTarget {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
+}
+
+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) }
+}
+
+impl fmt::Octal for CompactTarget {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Octal::fmt(&self.0, f) }
+}
+
+impl fmt::Binary for CompactTarget {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Binary::fmt(&self.0, f) }
+}
+
+#[cfg(feature = "encoding")]
+encoding::encoder_newtype_exact! {
+ /// The encoder for the [`CompactTarget`] type.
+ pub struct CompactTargetEncoder<'e>(encoding::ArrayEncoder<4>);
+}
+
+#[cfg(feature = "encoding")]
+impl encoding::Encodable for CompactTarget {
+ type Encoder<'e> = CompactTargetEncoder<'e>;
+ fn encoder(&self) -> Self::Encoder<'_> {
+ CompactTargetEncoder::new(encoding::ArrayEncoder::without_length_prefix(
+ self.to_consensus().to_le_bytes(),
+ ))
+ }
+}
+
+/// The decoder for the [`CompactTarget`] type.
+#[cfg(feature = "encoding")]
+pub struct CompactTargetDecoder(encoding::ArrayDecoder<4>);
+
+#[cfg(feature = "encoding")]
+impl CompactTargetDecoder {
+ /// Constructs a new [`CompactTarget`] decoder.
+ pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
+}
+
+#[cfg(feature = "encoding")]
+impl Default for CompactTargetDecoder {
+ fn default() -> Self { Self::new() }
+}
+
+#[cfg(feature = "encoding")]
+impl encoding::Decoder for CompactTargetDecoder {
+ type Output = CompactTarget;
+ type Error = CompactTargetDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(CompactTargetDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let n = u32::from_le_bytes(self.0.end().map_err(CompactTargetDecoderError)?);
+ Ok(CompactTarget::from_consensus(n))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+#[cfg(feature = "encoding")]
+impl encoding::Decodable for CompactTarget {
+ type Decoder = CompactTargetDecoder;
+ fn decoder() -> Self::Decoder { CompactTargetDecoder(encoding::ArrayDecoder::<4>::new()) }
+}
+
+/// An error consensus decoding an `CompactTarget`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[cfg(feature = "encoding")]
+pub struct CompactTargetDecoderError(encoding::UnexpectedEofError);
+
+#[cfg(feature = "encoding")]
+impl From<Infallible> for CompactTargetDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+#[cfg(feature = "encoding")]
+impl fmt::Display for CompactTargetDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_err!(f, "sequence decoder error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+#[cfg(feature = "encoding")]
+impl std::error::Error for CompactTargetDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for CompactTarget {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_consensus(u.arbitrary()?))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[cfg(feature = "alloc")]
+ use alloc::format;
+ #[cfg(feature = "alloc")]
+ use alloc::string::ToString;
+ #[cfg(feature = "std")]
+ use std::error::Error as _;
+
+ #[cfg(feature = "encoding")]
+ use encoding::Decoder as _;
+
+ use super::*;
+
+ #[test]
+ #[cfg(feature = "encoding")]
+ fn compact_target_decoder_read_limit() {
+ // read_limit is one u32 = 4 bytes for empty decoder
+ assert_eq!(CompactTargetDecoder::default().read_limit(), 4);
+ assert_eq!(<CompactTarget as encoding::Decodable>::decoder().read_limit(), 4);
+ }
+
+ #[test]
+ #[cfg(feature = "encoding")]
+ fn compact_target_decoder_round_trip() {
+ let bits: u32 = 0x1d00_ffff;
+ let compact_target =
+ encoding::decode_from_slice::<CompactTarget>(&bits.to_le_bytes()).unwrap();
+ assert_eq!(compact_target.to_consensus(), bits);
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ #[allow(deprecated)]
+ fn compact_target_to_hex() {
+ let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
+ assert_eq!(compact_target.to_hex(), "1d00ffff");
+ }
+
+ #[test]
+ #[cfg(feature = "encoding")]
+ #[cfg(feature = "alloc")]
+ fn compact_target_decoder_error_display_and_source() {
+ let mut slice = [0u8; 3].as_slice();
+ let mut decoder = CompactTargetDecoder::new();
+
+ assert!(decoder.push_bytes(&mut slice).unwrap());
+
+ let err = decoder.end().unwrap_err();
+ assert!(!err.to_string().is_empty());
+ #[cfg(feature = "std")]
+ assert!(err.source().is_some());
+ }
+
+ #[test]
+ fn compact_target_ordering() {
+ let lower = CompactTarget::from_consensus(0x1d00_fffe);
+ let lower_copy = CompactTarget::from_consensus(0x1d00_fffe);
+ let higher = CompactTarget::from_consensus(0x1d00_ffff);
+
+ assert!(lower < higher);
+ assert!(lower == lower_copy);
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn compact_target_formatting() {
+ let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
+ assert_eq!(format!("{}", compact_target), "486604799");
+ assert_eq!(format!("{:x}", compact_target), "1d00ffff");
+ assert_eq!(format!("{:#x}", compact_target), "0x1d00ffff");
+ assert_eq!(format!("{:X}", compact_target), "1D00FFFF");
+ assert_eq!(format!("{:#X}", compact_target), "0x1D00FFFF");
+ assert_eq!(format!("{:o}", compact_target), "3500177777");
+ assert_eq!(format!("{:#o}", compact_target), "0o3500177777");
+ assert_eq!(format!("{:b}", compact_target), "11101000000001111111111111111");
+ assert_eq!(format!("{:#b}", compact_target), "0b11101000000001111111111111111");
+ assert_eq!(compact_target.to_consensus(), 0x1d00_ffff);
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn compact_target_lower_hex_and_upper_hex() {
+ assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0_F456)), "01d0f456");
+ assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0_f456)), "01D0F456");
+ }
+}
diff --git a/units/tests/api.rs b/units/tests/api.rs
index b38909f3..6c5ddae4 100644
--- a/units/tests/api.rs
+++ b/units/tests/api.rs
@@ -14,7 +14,7 @@ use arbitrary::{Arbitrary, Unstructured};
// These imports test "typical" usage by user code.
use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::LockTime`.
use bitcoin_units::{
- amount, block, fee_rate, locktime, parse_int, result, sequence, time, weight, Amount,
+ amount, block, fee_rate, locktime, parse_int, pow, result, sequence, time, weight, Amount,
BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime, FeeRate, NumOpResult,
Sequence, SignedAmount, Weight,
};
@@ -58,9 +58,10 @@ struct Structs {
j: locktime::absolute::MedianTimePast,
k: locktime::relative::NumberOf512Seconds,
l: locktime::relative::NumberOfBlocks,
- m: sequence::Sequence,
- n: time::BlockTime,
- o: weight::Weight,
+ m: pow::CompactTarget,
+ n: sequence::Sequence,
+ o: time::BlockTime,
+ p: weight::Weight,
}
impl Structs {
@@ -78,9 +79,10 @@ impl Structs {
j: absolute::MedianTimePast::MAX,
k: relative::NumberOf512Seconds::MAX,
l: relative::NumberOfBlocks::MAX,
- m: sequence::Sequence::MAX,
- n: BlockTime::from_u32(u32::MAX),
- o: Weight::MAX,
+ m: pow::CompactTarget::from_consensus(u32::MAX),
+ n: sequence::Sequence::MAX,
+ o: BlockTime::from_u32(u32::MAX),
+ p: Weight::MAX,
}
}
}
@@ -113,8 +115,9 @@ struct CommonTraits {
j: locktime::absolute::MedianTimePast,
k: locktime::relative::NumberOf512Seconds,
l: locktime::relative::NumberOfBlocks,
- m: time::BlockTime,
- n: weight::Weight,
+ m: pow::CompactTarget,
+ n: time::BlockTime,
+ o: weight::Weight,
}
/// A struct that includes all types that implement `Default`.
@@ -156,6 +159,8 @@ struct Errors {
t: parse_int::ParseIntError,
u: parse_int::PrefixedHexError,
v: parse_int::UnprefixedHexError,
+ #[cfg(feature = "encoding")]
+ w: pow::CompactTargetDecoderError,
}
/// A struct that includes all public decoder error types.
@@ -173,15 +178,15 @@ struct DecoderErrors {
#[test]
fn api_can_use_modules_from_crate_root() {
use bitcoin_units::{
- amount, block, fee_rate, locktime, parse_int, result, sequence, time, weight,
+ amount, block, fee_rate, locktime, parse_int, pow, result, sequence, time, weight,
};
}
#[test]
fn api_can_use_types_from_crate_root() {
use bitcoin_units::{
- Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime, FeeRate,
- NumOpResult, Sequence, SignedAmount, Weight,
+ Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime,
+ CompactTarget, FeeRate, NumOpResult, Sequence, SignedAmount, Weight,
};
}
@@ -262,6 +267,13 @@ fn api_can_use_all_types_from_module_parse() {
use bitcoin_units::parse_int::{ParseIntError, PrefixedHexError, UnprefixedHexError};
}
+#[test]
+fn api_can_use_all_types_from_module_pow() {
+ use bitcoin_units::pow::CompactTarget;
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::pow::{CompactTargetDecoder, CompactTargetDecoderError, CompactTargetEncoder};
+}
+
#[test]
fn api_can_use_all_types_from_module_time() {
use bitcoin_units::time::BlockTime;
@@ -320,6 +332,8 @@ fn api_all_non_error_types_have_non_empty_debug() {
assert!(!debug.is_empty());
let debug = format!("{:?}", t.b.o);
assert!(!debug.is_empty());
+ let debug = format!("{:?}", t.b.p);
+ assert!(!debug.is_empty());
}
#[test]
@@ -387,9 +401,10 @@ impl<'a> Arbitrary<'a> for Structs {
j: absolute::MedianTimePast::arbitrary(u)?,
k: relative::NumberOf512Seconds::arbitrary(u)?,
l: relative::NumberOfBlocks::arbitrary(u)?,
- m: sequence::Sequence::arbitrary(u)?,
- n: BlockTime::arbitrary(u)?,
- o: Weight::arbitrary(u)?,
+ m: pow::CompactTarget::from_consensus(u.int_in_range(0..=u32::MAX)?),
+ n: sequence::Sequence::arbitrary(u)?,
+ o: BlockTime::arbitrary(u)?,
+ p: Weight::arbitrary(u)?,
};
Ok(a)
}
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.