From 5a8f33f38011a3562e66c8054f0c57a666809027 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 1 May 2025 17:37:05 +0000 Subject: [PATCH] units: rename absolute::Mtp consensus functions There is no "consensus encoding" for a MTP. The intention for these methods was that a user could interpret the MTP as a locktime and then consensus-encode that locktime. However, it was instead interpreted as the MTP representing a *blocktime* as it is consensus-encoded in a block header. Evidence of this misinterpretation is in several doccomments, which casually refer to the Mtp (which used to be just called Time) as a "block time", which is simply incorrect. --- primitives/src/locktime/absolute.rs | 38 +++++++++++++++----------- units/src/locktime/absolute.rs | 41 ++++++++++++++++++----------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/primitives/src/locktime/absolute.rs b/primitives/src/locktime/absolute.rs index 6ea4d3c08..242536a1d 100644 --- a/primitives/src/locktime/absolute.rs +++ b/primitives/src/locktime/absolute.rs @@ -147,7 +147,7 @@ impl LockTime { if units::locktime::absolute::is_block_height(n) { Self::Blocks(Height::from_consensus(n).expect("n is valid")) } else { - Self::Seconds(Mtp::from_consensus(n).expect("n is valid")) + Self::Seconds(Mtp::from_u32(n).expect("n is valid")) } } @@ -174,13 +174,19 @@ impl LockTime { Ok(LockTime::Blocks(height)) } - /// Constructs a new `LockTime` from `n`, expecting `n` to be a valid block time. + #[inline] + #[deprecated(since = "TBD", note = "use `from_mtp` instead")] + #[doc(hidden)] + pub fn from_time(n: u32) -> Result { Self::from_mtp(n) } + + /// Constructs a new `LockTime` from `n`, expecting `n` to be a median-time-past (MTP) + /// which is in range for a locktime. /// /// # Note /// - /// If the locktime is set to a timestamp `T`, - /// the transaction can be included in a block only if the median time past (MTP) of the - /// last 11 blocks is greater than `T`. + /// If the locktime is set to an MTP `T`, the transaction can be included in a block only if + /// the MTP of last recent 11 blocks is greater than `T`. + /// /// It is possible to broadcast the transaction once the MTP is greater than `T`.[see BIP-113] /// /// [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) @@ -195,8 +201,8 @@ impl LockTime { /// assert!(absolute::LockTime::from_time(741521).is_err()); /// ``` #[inline] - pub fn from_time(n: u32) -> Result { - let time = Mtp::from_consensus(n)?; + pub fn from_mtp(n: u32) -> Result { + let time = Mtp::from_u32(n)?; Ok(LockTime::Seconds(time)) } @@ -309,7 +315,7 @@ impl LockTime { pub fn to_consensus_u32(self) -> u32 { match self { LockTime::Blocks(ref h) => h.to_consensus_u32(), - LockTime::Seconds(ref t) => t.to_consensus_u32(), + LockTime::Seconds(ref t) => t.to_u32(), } } } @@ -410,7 +416,7 @@ mod tests { #[test] fn display_and_alternate() { let lock_by_height = LockTime::from_height(741_521).unwrap(); - let lock_by_time = LockTime::from_time(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC. + let lock_by_time = LockTime::from_mtp(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC. assert_eq!(format!("{}", lock_by_height), "741521"); assert_eq!(format!("{:#}", lock_by_height), "block-height 741521"); @@ -466,9 +472,9 @@ mod tests { assert!(LockTime::from_height(500_000_000).is_err()); // The threshold. assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold. - assert!(LockTime::from_time(499_999_999).is_err()); // Below the threshold. - assert!(LockTime::from_time(500_000_000).is_ok()); // The threshold. - assert!(LockTime::from_time(500_000_001).is_ok()); // Above the threshold. + assert!(LockTime::from_mtp(499_999_999).is_err()); // Below the threshold. + assert!(LockTime::from_mtp(500_000_000).is_ok()); // The threshold. + assert!(LockTime::from_mtp(500_000_001).is_ok()); // Above the threshold. } #[test] @@ -500,7 +506,7 @@ mod tests { let lock_by_height = LockTime::from(height); let t: u32 = 1_653_195_600; // May 22nd, 5am UTC. - let time = Mtp::from_consensus(t).unwrap(); + let time = Mtp::from_u32(t).unwrap(); assert!(!lock_by_height.is_satisfied_by(height_below, time)); assert!(lock_by_height.is_satisfied_by(height, time)); @@ -509,9 +515,9 @@ mod tests { #[test] fn satisfied_by_time() { - let time_before = Mtp::from_consensus(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC. - let time = Mtp::from_consensus(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC. - let time_after = Mtp::from_consensus(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC. + let time_before = Mtp::from_u32(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC. + let time = Mtp::from_u32(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC. + let time_after = Mtp::from_u32(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC. let lock_by_time = LockTime::from(time); diff --git a/units/src/locktime/absolute.rs b/units/src/locktime/absolute.rs index 07569062b..81e090e87 100644 --- a/units/src/locktime/absolute.rs +++ b/units/src/locktime/absolute.rs @@ -147,20 +147,31 @@ impl Mtp { /// The maximum MTP allowable in a locktime (Sun Feb 07 2106 06:28:15 GMT+0000). pub const MAX: Self = Mtp(u32::MAX); - /// Constructs a new [`Mtp`] from a hex string. + /// Constructs a new [`Mtp`] from a big-endian hex-encoded `u32`. /// /// The input string may or may not contain a typical hex prefix e.g., `0x`. /// /// # Errors /// /// If the input string is not a valid hex representation of a block time. - pub fn from_hex(s: &str) -> Result { parse_hex(s, Self::from_consensus) } + pub fn from_hex(s: &str) -> Result { parse_hex(s, Self::from_u32) } - /// Constructs a new block time. + #[deprecated(since = "TBD", note = "use `from_u32` instead")] + #[doc(hidden)] + pub const fn from_consensus(n: u32) -> Result { Self::from_u32(n) } + + #[deprecated(since = "TBD", note = "use `to_u32` instead")] + #[doc(hidden)] + pub const fn to_consensus_u32(self) -> u32 { self.to_u32() } + + /// Constructs a new MTP directly from a `u32` value. + /// + /// This function, with [`Mtp::to_u32`], is used to obtain a raw MTP value. It is + /// **not** used to convert to or from a block timestamp, which is not a MTP. /// /// # Errors /// - /// If `n` does not encode a valid UNIX time stamp. + /// If `n` is not in the allowable range of MTPs in a locktime: `[500_000_000, 2^32 - 1]`. /// /// # Examples /// @@ -168,11 +179,11 @@ impl Mtp { /// use bitcoin_units::locktime::absolute::Mtp; /// /// let t: u32 = 1653195600; // May 22nd, 5am UTC. - /// let time = Mtp::from_consensus(t).expect("invalid time value"); + /// let time = Mtp::from_u32(t).expect("invalid time value"); /// assert_eq!(time.to_consensus_u32(), t); /// ``` #[inline] - pub const fn from_consensus(n: u32) -> Result { + pub const fn from_u32(n: u32) -> Result { if is_block_time(n) { Ok(Self(n)) } else { @@ -180,16 +191,16 @@ impl Mtp { } } - /// Converts this [`Mtp`] to its inner `u32` value. + /// Converts this [`Mtp`] to a raw `u32` value. #[inline] - pub const fn to_consensus_u32(self) -> u32 { self.0 } + pub const fn to_u32(self) -> u32 { self.0 } } impl fmt::Display for Mtp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } -crate::impl_parse_str!(Mtp, ParseTimeError, parser(Mtp::from_consensus)); +crate::impl_parse_str!(Mtp, ParseTimeError, parser(Mtp::from_u32)); #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Mtp { @@ -198,7 +209,7 @@ impl<'de> serde::Deserialize<'de> for Mtp { D: serde::Deserializer<'de>, { let u = u32::deserialize(deserializer)?; - Mtp::from_consensus(u).map_err(serde::de::Error::custom) + Mtp::from_u32(u).map_err(serde::de::Error::custom) } } @@ -208,7 +219,7 @@ impl serde::Serialize for Mtp { where S: serde::Serializer, { - self.to_consensus_u32().serialize(serializer) + self.to_u32().serialize(serializer) } } @@ -431,10 +442,10 @@ impl<'a> Arbitrary<'a> for Mtp { 0 => Ok(Mtp::MIN), 1 => Ok(Mtp::MAX), _ => { - let min = Mtp::MIN.to_consensus_u32(); - let max = Mtp::MAX.to_consensus_u32(); + let min = Mtp::MIN.to_u32(); + let max = Mtp::MAX.to_u32(); let t = u.int_in_range(min..=max)?; - Ok(Mtp::from_consensus(t).unwrap()) + Ok(Mtp::from_u32(t).unwrap()) } } } @@ -450,7 +461,7 @@ mod tests { #[test] fn time_from_str_hex_happy_path() { let actual = Mtp::from_hex("0x6289C350").unwrap(); - let expected = Mtp::from_consensus(0x6289_C350).unwrap(); + let expected = Mtp::from_u32(0x6289_C350).unwrap(); assert_eq!(actual, expected); }