Skip to content

Commit 49e420a

Browse files
committed
Merge rust-bitcoin#3059: Move CompactTarget to primitives
2ec901f Move the CompactTarget type to primitives (Tobin C. Harding) a00bd7c Introduce CompactTargetExt trait (Tobin C. Harding) 100ce03 Run cargo +nightly fmt (Tobin C. Harding) 9c4a629 Wrap CompactTarget impl block in temporary module (Tobin C. Harding) 578143c Separate CompactTarget impl blocks (Tobin C. Harding) 22d5646 Stop using CompactTarget inner field (Tobin C. Harding) 244d7db Remove generic test impl (Tobin C. Harding) 3d85ee3 primitives: Fix alloc feature (Tobin C. Harding) Pull request description: Done in preparation for moving `BlockHash` and `block::Header` to `primitives`. - Patch 1 introduces an extension trait using `define_extension_trait!` - Patch 2 is the trivial copy and past to move the type to `primitives` This one shouldn't be to arduous to review, thanks. ACKs for top commit: Kixunil: ACK 2ec901f apoelstra: ACK 2ec901f successfully ran local tests Tree-SHA512: b0e4f1af0b268e249a056cae71d7cafd1b025c4a079e5393ce80cd0b9c9bb6d2c6306531dc6786d986ff8a094b61866a86285b20d54037ef1395d127876bfd9c
2 parents 5d8f4b2 + 2ec901f commit 49e420a

File tree

3 files changed

+153
-130
lines changed

3 files changed

+153
-130
lines changed

bitcoin/src/pow.rs

Lines changed: 107 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ use units::parse::{self, ParseIntError, PrefixedHexError, UnprefixedHexError};
1515

1616
use crate::block::{BlockHash, Header};
1717
use crate::consensus::encode::{self, Decodable, Encodable};
18+
use crate::internal_macros::define_extension_trait;
1819
use crate::network::Params;
1920

21+
#[rustfmt::skip] // Keep public re-exports separate.
22+
#[doc(inline)]
23+
pub use primitives::CompactTarget;
24+
2025
/// Implement traits and methods shared by `Target` and `Work`.
2126
macro_rules! do_impl {
2227
($ty:ident) => {
@@ -164,7 +169,7 @@ impl Target {
164169
///
165170
/// ref: <https://developer.bitcoin.org/reference/block_chain.html#target-nbits>
166171
pub fn from_compact(c: CompactTarget) -> Target {
167-
let bits = c.0;
172+
let bits = c.to_consensus();
168173
// This is a floating-point "compact" encoding originally used by
169174
// OpenSSL, which satoshi put into consensus code, so we're stuck
170175
// with it. The exponent needs to have 3 subtracted from it, hence
@@ -204,7 +209,7 @@ impl Target {
204209
size += 1;
205210
}
206211

207-
CompactTarget(compact | (size << 24))
212+
CompactTarget::from_consensus(compact | (size << 24))
208213
}
209214

210215
/// Returns true if block hash is less than or equal to this [`Target`].
@@ -329,118 +334,97 @@ impl Target {
329334
}
330335
do_impl!(Target);
331336

332-
/// Encoding of 256-bit target as 32-bit float.
333-
///
334-
/// This is used to encode a target into the block header. Satoshi made this part of consensus code
335-
/// in the original version of Bitcoin, likely copying an idea from OpenSSL.
336-
///
337-
/// OpenSSL's bignum (BN) type has an encoding, which is even called "compact" as in bitcoin, which
338-
/// is exactly this format.
339-
///
340-
/// # Note on order/equality
341-
///
342-
/// Usage of the ordering and equality traits for this type may be surprising. Converting between
343-
/// `CompactTarget` and `Target` is lossy *in both directions* (there are multiple `CompactTarget`
344-
/// values that map to the same `Target` value). Ordering and equality for this type are defined in
345-
/// terms of the underlying `u32`.
346-
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
347-
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
348-
pub struct CompactTarget(u32);
349-
350-
impl CompactTarget {
351-
/// Creates a `CompactTarget` from a prefixed hex string.
352-
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
353-
let target = parse::hex_u32_prefixed(s)?;
354-
Ok(Self::from_consensus(target))
355-
}
337+
define_extension_trait! {
338+
/// Extension functionality for the [`CompactTarget`] type.
339+
pub trait CompactTargetExt impl for CompactTarget {
340+
/// Creates a `CompactTarget` from a prefixed hex string.
341+
fn from_hex(s: &str) -> Result<CompactTarget, PrefixedHexError> {
342+
let target = parse::hex_u32_prefixed(s)?;
343+
Ok(Self::from_consensus(target))
344+
}
356345

357-
/// Creates a `CompactTarget` from an unprefixed hex string.
358-
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
359-
let target = parse::hex_u32_unprefixed(s)?;
360-
Ok(Self::from_consensus(target))
361-
}
346+
/// Creates a `CompactTarget` from an unprefixed hex string.
347+
fn from_unprefixed_hex(s: &str) -> Result<CompactTarget, UnprefixedHexError> {
348+
let target = parse::hex_u32_unprefixed(s)?;
349+
Ok(Self::from_consensus(target))
350+
}
362351

363-
/// Computes the [`CompactTarget`] from a difficulty adjustment.
364-
///
365-
/// ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
366-
///
367-
/// Given the previous Target, represented as a [`CompactTarget`], the difficulty is adjusted
368-
/// by taking the timespan between them, and multipling the current [`CompactTarget`] by a factor
369-
/// of the net timespan and expected timespan. The [`CompactTarget`] may not adjust by more than
370-
/// a factor of 4, or adjust beyond the maximum threshold for the network.
371-
///
372-
/// # Note
373-
///
374-
/// Under the consensus rules, the difference in the number of blocks between the headers does
375-
/// not equate to the `difficulty_adjustment_interval` of [`Params`]. This is due to an off-by-one
376-
/// error, and, the expected number of blocks in between headers is `difficulty_adjustment_interval - 1`
377-
/// when calculating the difficulty adjustment.
378-
///
379-
/// Take the example of the first difficulty adjustment. Block 2016 introduces a new [`CompactTarget`],
380-
/// which takes the net timespan between Block 2015 and Block 0, and recomputes the difficulty.
381-
///
382-
/// # Returns
383-
///
384-
/// The expected [`CompactTarget`] recalculation.
385-
pub fn from_next_work_required(
386-
last: CompactTarget,
387-
timespan: u64,
388-
params: impl AsRef<Params>,
389-
) -> CompactTarget {
390-
let params = params.as_ref();
391-
if params.no_pow_retargeting {
392-
return last;
352+
/// Computes the [`CompactTarget`] from a difficulty adjustment.
353+
///
354+
/// ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
355+
///
356+
/// Given the previous Target, represented as a [`CompactTarget`], the difficulty is adjusted
357+
/// by taking the timespan between them, and multipling the current [`CompactTarget`] by a factor
358+
/// of the net timespan and expected timespan. The [`CompactTarget`] may not adjust by more than
359+
/// a factor of 4, or adjust beyond the maximum threshold for the network.
360+
///
361+
/// # Note
362+
///
363+
/// Under the consensus rules, the difference in the number of blocks between the headers does
364+
/// not equate to the `difficulty_adjustment_interval` of [`Params`]. This is due to an off-by-one
365+
/// error, and, the expected number of blocks in between headers is `difficulty_adjustment_interval - 1`
366+
/// when calculating the difficulty adjustment.
367+
///
368+
/// Take the example of the first difficulty adjustment. Block 2016 introduces a new [`CompactTarget`],
369+
/// which takes the net timespan between Block 2015 and Block 0, and recomputes the difficulty.
370+
///
371+
/// # Returns
372+
///
373+
/// The expected [`CompactTarget`] recalculation.
374+
fn from_next_work_required(
375+
last: CompactTarget,
376+
timespan: u64,
377+
params: impl AsRef<Params>,
378+
) -> CompactTarget {
379+
let params = params.as_ref();
380+
if params.no_pow_retargeting {
381+
return last;
382+
}
383+
// Comments relate to the `pow.cpp` file from Core.
384+
// ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
385+
let min_timespan = params.pow_target_timespan >> 2; // Lines 56/57
386+
let max_timespan = params.pow_target_timespan << 2; // Lines 58/59
387+
let actual_timespan = timespan.clamp(min_timespan, max_timespan);
388+
let prev_target: Target = last.into();
389+
let maximum_retarget = prev_target.max_transition_threshold(params); // bnPowLimit
390+
let retarget = prev_target.0; // bnNew
391+
let retarget = retarget.mul(actual_timespan.into());
392+
let retarget = retarget.div(params.pow_target_timespan.into());
393+
let retarget = Target(retarget);
394+
if retarget.ge(&maximum_retarget) {
395+
return maximum_retarget.to_compact_lossy();
396+
}
397+
retarget.to_compact_lossy()
393398
}
394-
// Comments relate to the `pow.cpp` file from Core.
395-
// ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
396-
let min_timespan = params.pow_target_timespan >> 2; // Lines 56/57
397-
let max_timespan = params.pow_target_timespan << 2; // Lines 58/59
398-
let actual_timespan = timespan.clamp(min_timespan, max_timespan);
399-
let prev_target: Target = last.into();
400-
let maximum_retarget = prev_target.max_transition_threshold(params); // bnPowLimit
401-
let retarget = prev_target.0; // bnNew
402-
let retarget = retarget.mul(actual_timespan.into());
403-
let retarget = retarget.div(params.pow_target_timespan.into());
404-
let retarget = Target(retarget);
405-
if retarget.ge(&maximum_retarget) {
406-
return maximum_retarget.to_compact_lossy();
399+
400+
/// Computes the [`CompactTarget`] from a difficulty adjustment,
401+
/// assuming these are the relevant block headers.
402+
///
403+
/// Given two headers, representing the start and end of a difficulty adjustment epoch,
404+
/// compute the [`CompactTarget`] based on the net time between them and the current
405+
/// [`CompactTarget`].
406+
///
407+
/// # Note
408+
///
409+
/// See [`CompactTarget::from_next_work_required`]
410+
///
411+
/// For example, to successfully compute the first difficulty adjustment on the Bitcoin network,
412+
/// one would pass the header for Block 2015 as `current` and the header for Block 0 as
413+
/// `last_epoch_boundary`.
414+
///
415+
/// # Returns
416+
///
417+
/// The expected [`CompactTarget`] recalculation.
418+
fn from_header_difficulty_adjustment(
419+
last_epoch_boundary: Header,
420+
current: Header,
421+
params: impl AsRef<Params>,
422+
) -> CompactTarget {
423+
let timespan = current.time - last_epoch_boundary.time;
424+
let bits = current.bits;
425+
CompactTarget::from_next_work_required(bits, timespan.into(), params)
407426
}
408-
retarget.to_compact_lossy()
409427
}
410-
411-
/// Computes the [`CompactTarget`] from a difficulty adjustment,
412-
/// assuming these are the relevant block headers.
413-
///
414-
/// Given two headers, representing the start and end of a difficulty adjustment epoch,
415-
/// compute the [`CompactTarget`] based on the net time between them and the current
416-
/// [`CompactTarget`].
417-
///
418-
/// # Note
419-
///
420-
/// See [`CompactTarget::from_next_work_required`]
421-
///
422-
/// For example, to successfully compute the first difficulty adjustment on the Bitcoin network,
423-
/// one would pass the header for Block 2015 as `current` and the header for Block 0 as
424-
/// `last_epoch_boundary`.
425-
///
426-
/// # Returns
427-
///
428-
/// The expected [`CompactTarget`] recalculation.
429-
pub fn from_header_difficulty_adjustment(
430-
last_epoch_boundary: Header,
431-
current: Header,
432-
params: impl AsRef<Params>,
433-
) -> CompactTarget {
434-
let timespan = current.time - last_epoch_boundary.time;
435-
let bits = current.bits;
436-
CompactTarget::from_next_work_required(bits, timespan.into(), params)
437-
}
438-
439-
/// Creates a [`CompactTarget`] from a consensus encoded `u32`.
440-
pub fn from_consensus(bits: u32) -> Self { Self(bits) }
441-
442-
/// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
443-
pub fn to_consensus(self) -> u32 { self.0 }
444428
}
445429

446430
impl From<CompactTarget> for Target {
@@ -450,27 +434,17 @@ impl From<CompactTarget> for Target {
450434
impl Encodable for CompactTarget {
451435
#[inline]
452436
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
453-
self.0.consensus_encode(w)
437+
self.to_consensus().consensus_encode(w)
454438
}
455439
}
456440

457441
impl Decodable for CompactTarget {
458442
#[inline]
459443
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
460-
u32::consensus_decode(r).map(CompactTarget)
444+
u32::consensus_decode(r).map(CompactTarget::from_consensus)
461445
}
462446
}
463447

464-
impl fmt::LowerHex for CompactTarget {
465-
#[inline]
466-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
467-
}
468-
469-
impl fmt::UpperHex for CompactTarget {
470-
#[inline]
471-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
472-
}
473-
474448
/// Big-endian 256 bit integer type.
475449
// (high, low): u.0 contains the high bits, u.1 contains the low bits.
476450
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
@@ -1099,8 +1073,12 @@ impl kani::Arbitrary for U256 {
10991073
mod tests {
11001074
use super::*;
11011075

1102-
impl<T: Into<u128>> From<T> for Target {
1103-
fn from(x: T) -> Self { Self(U256::from(x)) }
1076+
impl From<u64> for Target {
1077+
fn from(x: u64) -> Self { Self(U256::from(x)) }
1078+
}
1079+
1080+
impl From<u32> for Target {
1081+
fn from(x: u32) -> Self { Self(U256::from(x)) }
11041082
}
11051083

11061084
impl<T: Into<u128>> From<T> for Work {
@@ -1721,25 +1699,25 @@ mod tests {
17211699
#[test]
17221700
fn compact_target_from_hex_lower() {
17231701
let target = CompactTarget::from_hex("0x010034ab").unwrap();
1724-
assert_eq!(target, CompactTarget(0x010034ab));
1702+
assert_eq!(target, CompactTarget::from_consensus(0x010034ab));
17251703
}
17261704

17271705
#[test]
17281706
fn compact_target_from_hex_upper() {
17291707
let target = CompactTarget::from_hex("0X010034AB").unwrap();
1730-
assert_eq!(target, CompactTarget(0x010034ab));
1708+
assert_eq!(target, CompactTarget::from_consensus(0x010034ab));
17311709
}
17321710

17331711
#[test]
17341712
fn compact_target_from_unprefixed_hex_lower() {
17351713
let target = CompactTarget::from_unprefixed_hex("010034ab").unwrap();
1736-
assert_eq!(target, CompactTarget(0x010034ab));
1714+
assert_eq!(target, CompactTarget::from_consensus(0x010034ab));
17371715
}
17381716

17391717
#[test]
17401718
fn compact_target_from_unprefixed_hex_upper() {
17411719
let target = CompactTarget::from_unprefixed_hex("010034AB").unwrap();
1742-
assert_eq!(target, CompactTarget(0x010034ab));
1720+
assert_eq!(target, CompactTarget::from_consensus(0x010034ab));
17431721
}
17441722

17451723
#[test]
@@ -1751,8 +1729,8 @@ mod tests {
17511729

17521730
#[test]
17531731
fn compact_target_lower_hex_and_upper_hex() {
1754-
assert_eq!(format!("{:08x}", CompactTarget(0x01D0F456)), "01d0f456");
1755-
assert_eq!(format!("{:08X}", CompactTarget(0x01d0f456)), "01D0F456");
1732+
assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0F456)), "01d0f456");
1733+
assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0f456)), "01D0F456");
17561734
}
17571735

17581736
#[test]

primitives/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern crate serde;
3131
#[cfg(feature = "alloc")]
3232
pub mod locktime;
3333
pub mod opcodes;
34+
pub mod pow;
3435
pub mod sequence;
3536

3637
#[doc(inline)]
@@ -40,7 +41,10 @@ pub use units::*;
4041
#[cfg(feature = "alloc")]
4142
pub use self::locktime::{absolute, relative};
4243
#[doc(inline)]
43-
pub use self::sequence::Sequence;
44+
pub use self::{
45+
pow::CompactTarget,
46+
sequence::Sequence,
47+
};
4448

4549
#[rustfmt::skip]
4650
#[allow(unused_imports)]

primitives/src/pow.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Proof-of-work related integer types.
4+
5+
use core::fmt;
6+
7+
/// Encoding of 256-bit target as 32-bit float.
8+
///
9+
/// This is used to encode a target into the block header. Satoshi made this part of consensus code
10+
/// in the original version of Bitcoin, likely copying an idea from OpenSSL.
11+
///
12+
/// OpenSSL's bignum (BN) type has an encoding, which is even called "compact" as in bitcoin, which
13+
/// is exactly this format.
14+
///
15+
/// # Note on order/equality
16+
///
17+
/// Usage of the ordering and equality traits for this type may be surprising. Converting between
18+
/// `CompactTarget` and `Target` is lossy *in both directions* (there are multiple `CompactTarget`
19+
/// values that map to the same `Target` value). Ordering and equality for this type are defined in
20+
/// terms of the underlying `u32`.
21+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
22+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23+
pub struct CompactTarget(u32);
24+
25+
impl CompactTarget {
26+
/// Creates a [`CompactTarget`] from a consensus encoded `u32`.
27+
pub fn from_consensus(bits: u32) -> Self { Self(bits) }
28+
29+
/// Returns the consensus encoded `u32` representation of this [`CompactTarget`].
30+
pub fn to_consensus(self) -> u32 { self.0 }
31+
}
32+
33+
impl fmt::LowerHex for CompactTarget {
34+
#[inline]
35+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
36+
}
37+
38+
impl fmt::UpperHex for CompactTarget {
39+
#[inline]
40+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
41+
}

0 commit comments

Comments
 (0)