Skip to main content

bitcoin_units/
pow.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Proof-of-work related integer types.
4
5#[cfg(feature = "encoding")]
6use core::convert::Infallible;
7use core::fmt;
8
9#[cfg(feature = "arbitrary")]
10use arbitrary::{Arbitrary, Unstructured};
11#[cfg(feature = "encoding")]
12use internals::write_err;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
17
18/// Encoding of 256-bit target as 32-bit float.
19///
20/// This is used to encode a target into the block header. Satoshi made this part of consensus code
21/// in the original version of Bitcoin, likely copying an idea from OpenSSL.
22///
23/// OpenSSL's bignum (BN) type has an encoding, which is even called "compact" as in bitcoin, which
24/// is exactly this format.
25///
26/// # Note on order/equality
27///
28/// Usage of the ordering and equality traits for this type may be surprising. Converting between
29/// `CompactTarget` and `Target` is lossy *in both directions* (there are multiple `CompactTarget`
30/// values that map to the same `Target` value). Ordering and equality for this type are defined in
31/// terms of the underlying `u32`.
32#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct CompactTarget(u32);
35
36impl CompactTarget {
37    /// Constructs a new [`CompactTarget`] from a consensus encoded `u32`.
38    #[inline]
39    pub fn from_consensus(bits: u32) -> Self { Self(bits) }
40
41    /// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
42    #[inline]
43    pub const fn to_consensus(self) -> u32 { self.0 }
44
45    /// Gets the hex representation of this [`CompactTarget`].
46    #[cfg(feature = "alloc")]
47    #[inline]
48    #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
49    pub fn to_hex(self) -> alloc::string::String { alloc::format!("{:x}", self) }
50
51    /// Constructs a new `CompactTarget` from a prefixed hex string.
52    ///
53    /// # Errors
54    ///
55    /// - If the input string does not contain a `0x` (or `0X`) prefix.
56    /// - If the input string is not a valid hex encoding of a `u32`.
57    pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError>
58    where
59        Self: Sized
60    {
61        let target = parse_int::hex_u32_prefixed(s)?;
62        Ok(Self::from_consensus(target))
63    }
64
65    /// Constructs a new `CompactTarget` from an unprefixed hex string.
66    ///
67    /// # Errors
68    ///
69    /// - If the input string contains a `0x` (or `0X`) prefix.
70    /// - If the input string is not a valid hex encoding of a `u32`.
71    pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError>
72    where
73        Self: Sized
74    {
75        let target = parse_int::hex_u32_unprefixed(s)?;
76        Ok(Self::from_consensus(target))
77    }
78}
79
80impl fmt::Display for CompactTarget {
81    #[inline]
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
83}
84
85impl fmt::LowerHex for CompactTarget {
86    #[inline]
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
88}
89
90impl fmt::UpperHex for CompactTarget {
91    #[inline]
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
93}
94
95impl fmt::Octal for CompactTarget {
96    #[inline]
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Octal::fmt(&self.0, f) }
98}
99
100impl fmt::Binary for CompactTarget {
101    #[inline]
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Binary::fmt(&self.0, f) }
103}
104
105#[cfg(feature = "encoding")]
106encoding::encoder_newtype_exact! {
107    /// The encoder for the [`CompactTarget`] type.
108    pub struct CompactTargetEncoder<'e>(encoding::ArrayEncoder<4>);
109}
110
111#[cfg(feature = "encoding")]
112impl encoding::Encodable for CompactTarget {
113    type Encoder<'e> = CompactTargetEncoder<'e>;
114    fn encoder(&self) -> Self::Encoder<'_> {
115        CompactTargetEncoder::new(encoding::ArrayEncoder::without_length_prefix(
116            self.to_consensus().to_le_bytes(),
117        ))
118    }
119}
120
121/// The decoder for the [`CompactTarget`] type.
122#[cfg(feature = "encoding")]
123pub struct CompactTargetDecoder(encoding::ArrayDecoder<4>);
124
125#[cfg(feature = "encoding")]
126impl CompactTargetDecoder {
127    /// Constructs a new [`CompactTarget`] decoder.
128    pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
129}
130
131#[cfg(feature = "encoding")]
132impl Default for CompactTargetDecoder {
133    fn default() -> Self { Self::new() }
134}
135
136#[cfg(feature = "encoding")]
137impl encoding::Decoder for CompactTargetDecoder {
138    type Output = CompactTarget;
139    type Error = CompactTargetDecoderError;
140
141    #[inline]
142    fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
143        self.0.push_bytes(bytes).map_err(CompactTargetDecoderError)
144    }
145
146    #[inline]
147    fn end(self) -> Result<Self::Output, Self::Error> {
148        let n = u32::from_le_bytes(self.0.end().map_err(CompactTargetDecoderError)?);
149        Ok(CompactTarget::from_consensus(n))
150    }
151
152    #[inline]
153    fn read_limit(&self) -> usize { self.0.read_limit() }
154}
155
156#[cfg(feature = "encoding")]
157impl encoding::Decodable for CompactTarget {
158    type Decoder = CompactTargetDecoder;
159    fn decoder() -> Self::Decoder { CompactTargetDecoder(encoding::ArrayDecoder::<4>::new()) }
160}
161
162/// An error consensus decoding an `CompactTarget`.
163#[derive(Debug, Clone, PartialEq, Eq)]
164#[cfg(feature = "encoding")]
165pub struct CompactTargetDecoderError(encoding::UnexpectedEofError);
166
167#[cfg(feature = "encoding")]
168impl From<Infallible> for CompactTargetDecoderError {
169    fn from(never: Infallible) -> Self { match never {} }
170}
171
172#[cfg(feature = "encoding")]
173impl fmt::Display for CompactTargetDecoderError {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        write_err!(f, "sequence decoder error"; self.0)
176    }
177}
178
179#[cfg(feature = "std")]
180#[cfg(feature = "encoding")]
181impl std::error::Error for CompactTargetDecoderError {
182    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
183}
184
185#[cfg(feature = "arbitrary")]
186impl<'a> Arbitrary<'a> for CompactTarget {
187    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
188        Ok(Self::from_consensus(u.arbitrary()?))
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    #[cfg(feature = "alloc")]
195    use alloc::format;
196    #[cfg(feature = "alloc")]
197    use alloc::string::ToString;
198    #[cfg(feature = "std")]
199    use std::error::Error as _;
200
201    #[cfg(feature = "encoding")]
202    use encoding::Decoder as _;
203
204    use super::*;
205
206    #[test]
207    #[cfg(feature = "encoding")]
208    fn compact_target_decoder_read_limit() {
209        // read_limit is one u32 = 4 bytes for empty decoder
210        assert_eq!(CompactTargetDecoder::default().read_limit(), 4);
211        assert_eq!(<CompactTarget as encoding::Decodable>::decoder().read_limit(), 4);
212    }
213
214    #[test]
215    #[cfg(feature = "encoding")]
216    fn compact_target_decoder_round_trip() {
217        let bits: u32 = 0x1d00_ffff;
218        let compact_target =
219            encoding::decode_from_slice::<CompactTarget>(&bits.to_le_bytes()).unwrap();
220        assert_eq!(compact_target.to_consensus(), bits);
221    }
222
223    #[test]
224    #[cfg(feature = "alloc")]
225    #[allow(deprecated)]
226    fn compact_target_to_hex() {
227        let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
228        assert_eq!(compact_target.to_hex(), "1d00ffff");
229    }
230
231    #[test]
232    #[cfg(feature = "encoding")]
233    #[cfg(feature = "alloc")]
234    fn compact_target_decoder_error_display_and_source() {
235        let mut slice = [0u8; 3].as_slice();
236        let mut decoder = CompactTargetDecoder::new();
237
238        assert!(decoder.push_bytes(&mut slice).unwrap());
239
240        let err = decoder.end().unwrap_err();
241        assert!(!err.to_string().is_empty());
242        #[cfg(feature = "std")]
243        assert!(err.source().is_some());
244    }
245
246    #[test]
247    fn compact_target_ordering() {
248        let lower = CompactTarget::from_consensus(0x1d00_fffe);
249        let lower_copy = CompactTarget::from_consensus(0x1d00_fffe);
250        let higher = CompactTarget::from_consensus(0x1d00_ffff);
251
252        assert!(lower < higher);
253        assert!(lower == lower_copy);
254    }
255
256    #[test]
257    #[cfg(feature = "alloc")]
258    fn compact_target_formatting() {
259        let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
260        assert_eq!(format!("{}", compact_target), "486604799");
261        assert_eq!(format!("{:x}", compact_target), "1d00ffff");
262        assert_eq!(format!("{:#x}", compact_target), "0x1d00ffff");
263        assert_eq!(format!("{:X}", compact_target), "1D00FFFF");
264        assert_eq!(format!("{:#X}", compact_target), "0x1D00FFFF");
265        assert_eq!(format!("{:o}", compact_target), "3500177777");
266        assert_eq!(format!("{:#o}", compact_target), "0o3500177777");
267        assert_eq!(format!("{:b}", compact_target), "11101000000001111111111111111");
268        assert_eq!(format!("{:#b}", compact_target), "0b11101000000001111111111111111");
269        assert_eq!(compact_target.to_consensus(), 0x1d00_ffff);
270    }
271
272    #[test]
273    fn compact_target_from_hex_lower() {
274        let target = CompactTarget::from_hex("0x010034ab").unwrap();
275        assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
276    }
277
278    #[test]
279    fn compact_target_from_hex_upper() {
280        let target = CompactTarget::from_hex("0X010034AB").unwrap();
281        assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
282    }
283
284    #[test]
285    fn compact_target_from_unprefixed_hex_lower() {
286        let target = CompactTarget::from_unprefixed_hex("010034ab").unwrap();
287        assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
288    }
289
290    #[test]
291    fn compact_target_from_unprefixed_hex_upper() {
292        let target = CompactTarget::from_unprefixed_hex("010034AB").unwrap();
293        assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
294    }
295
296    #[test]
297    fn compact_target_from_hex_invalid_hex_should_err() {
298        let hex = "0xzbf9";
299        let result = CompactTarget::from_hex(hex);
300        assert!(result.is_err());
301    }
302
303    #[test]
304    #[cfg(feature = "alloc")]
305    fn compact_target_lower_hex_and_upper_hex() {
306        assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0_F456)), "01d0f456");
307        assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0_f456)), "01D0F456");
308    }
309}