From 5e47c4123d5cdd768d7f8523ce81e1b2069edaae Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Wed, 27 Nov 2024 07:34:47 -0600 Subject: [PATCH] Update CompactTarget::from_next_work_required to take timespan as i64 Bitcoin Core's consensus rules allow this timespan interval to be negative. This commit also updates Params::pow_target_timespan to be a u32. This field is almost compared to u64 and i64s, so changing this to a u32 will allow users to use `.into()` for converstions --- bitcoin/src/network/params.rs | 4 ++-- bitcoin/src/pow.rs | 33 +++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/bitcoin/src/network/params.rs b/bitcoin/src/network/params.rs index eecc42cb8..e322009be 100644 --- a/bitcoin/src/network/params.rs +++ b/bitcoin/src/network/params.rs @@ -113,7 +113,7 @@ pub struct Params { /// Expected amount of time to mine one block. pub pow_target_spacing: u64, /// Difficulty recalculation interval. - pub pow_target_timespan: u64, + pub pow_target_timespan: u32, /// Determines whether minimal difficulty may be used for blocks or not. pub allow_min_difficulty_blocks: bool, /// Determines whether retargeting is disabled for this network or not. @@ -261,7 +261,7 @@ impl Params { /// Calculates the number of blocks between difficulty adjustments. pub fn difficulty_adjustment_interval(&self) -> u64 { - self.pow_target_timespan / self.pow_target_spacing + u64::from(self.pow_target_timespan) / self.pow_target_spacing } } diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index 7566e73b2..243da4a3c 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -371,12 +371,14 @@ define_extension_trait! { /// 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. /// + /// To calculate the timespan, users should first convert their u32 timestamps to i64s before subtracting them + /// /// # Returns /// /// The expected [`CompactTarget`] recalculation. fn from_next_work_required( last: CompactTarget, - timespan: u64, + timespan: i64, params: impl AsRef, ) -> CompactTarget { let params = params.as_ref(); @@ -387,11 +389,11 @@ define_extension_trait! { // 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 actual_timespan = timespan.clamp(min_timespan.into(), max_timespan.into()); 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.mul(u128::try_from(actual_timespan).expect("clamped value won't be negative").into()); let retarget = retarget.div(params.pow_target_timespan.into()); let retarget = Target(retarget); if retarget.ge(&maximum_retarget) { @@ -1750,8 +1752,8 @@ mod tests { fn compact_target_from_upwards_difficulty_adjustment() { let params = Params::new(crate::Network::Signet); let starting_bits = CompactTarget::from_consensus(503543726); // Genesis compact target on Signet - let start_time: u64 = 1598918400; // Genesis block unix time - let end_time: u64 = 1599332177; // Block 2015 unix time + let start_time: i64 = 1598918400; // Genesis block unix time + let end_time: i64 = 1599332177; // Block 2015 unix time let timespan = end_time - start_time; // Faster than expected let adjustment = CompactTarget::from_next_work_required(starting_bits, timespan, ¶ms); let adjustment_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target @@ -1762,8 +1764,8 @@ mod tests { fn compact_target_from_downwards_difficulty_adjustment() { let params = Params::new(crate::Network::Signet); let starting_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target - let start_time: u64 = 1599332844; // Block 2016 unix time - let end_time: u64 = 1600591200; // Block 4031 unix time + let start_time: i64 = 1599332844; // Block 2016 unix time + let end_time: i64 = 1600591200; // Block 4031 unix time let timespan = end_time - start_time; // Slower than expected let adjustment = CompactTarget::from_next_work_required(starting_bits, timespan, ¶ms); let adjustment_bits = CompactTarget::from_consensus(503397348); // Block 4032 compact target @@ -1829,7 +1831,18 @@ mod tests { fn compact_target_from_maximum_upward_difficulty_adjustment() { let params = Params::new(crate::Network::Signet); let starting_bits = CompactTarget::from_consensus(503403001); - let timespan = (0.2 * params.pow_target_timespan as f64) as u64; + let timespan = params.pow_target_timespan / 5; + let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), params); + let want = + Target::from_compact(starting_bits).min_transition_threshold().to_compact_lossy(); + assert_eq!(got, want); + } + + #[test] + fn compact_target_from_maximum_upward_difficulty_adjustment_with_negative_timespan() { + let params = Params::new(crate::Network::Signet); + let starting_bits = CompactTarget::from_consensus(503403001); + let timespan: i64 = -i64::from(params.pow_target_timespan); let got = CompactTarget::from_next_work_required(starting_bits, timespan, params); let want = Target::from_compact(starting_bits).min_transition_threshold().to_compact_lossy(); @@ -1841,7 +1854,7 @@ mod tests { let params = Params::new(crate::Network::Signet); let starting_bits = CompactTarget::from_consensus(403403001); // High difficulty for Signet let timespan = 5 * params.pow_target_timespan; // Really slow. - let got = CompactTarget::from_next_work_required(starting_bits, timespan, ¶ms); + let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), ¶ms); let want = Target::from_compact(starting_bits).max_transition_threshold(params).to_compact_lossy(); assert_eq!(got, want); @@ -1852,7 +1865,7 @@ mod tests { let params = Params::new(crate::Network::Signet); let starting_bits = CompactTarget::from_consensus(503543726); // Genesis compact target on Signet let timespan = 5 * params.pow_target_timespan; // Really slow. - let got = CompactTarget::from_next_work_required(starting_bits, timespan, ¶ms); + let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), ¶ms); let want = params.max_attainable_target.to_compact_lossy(); assert_eq!(got, want); }