bitcoin_units/locktime/
relative.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Provides [`Height`] and [`Time`] types used by the `rust-bitcoin` `relative::LockTime` type.
4
5use core::fmt;
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10/// A relative lock time lock-by-blockheight value.
11#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct Height(u16);
14
15impl Height {
16    /// Relative block height 0, can be included in any block.
17    pub const ZERO: Self = Height(0);
18
19    /// The minimum relative block height (0), can be included in any block.
20    pub const MIN: Self = Self::ZERO;
21
22    /// The maximum relative block height.
23    pub const MAX: Self = Height(u16::MAX);
24
25    /// Create a [`Height`] using a count of blocks.
26    #[inline]
27    pub const fn from_height(blocks: u16) -> Self { Height(blocks) }
28
29    /// Returns the inner `u16` value.
30    #[inline]
31    pub const fn value(self) -> u16 { self.0 }
32
33    /// Returns the `u32` value used to encode this locktime in an nSequence field or
34    /// argument to `OP_CHECKSEQUENCEVERIFY`.
35    #[inline]
36    pub const fn to_consensus_u32(&self) -> u32 {
37        self.0 as u32 // cast safety: u32 is wider than u16 on all architectures
38    }
39}
40
41impl From<u16> for Height {
42    #[inline]
43    fn from(value: u16) -> Self { Height(value) }
44}
45
46crate::impl_parse_str_from_int_infallible!(Height, u16, from);
47
48impl fmt::Display for Height {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
50}
51
52/// A relative lock time lock-by-blocktime value.
53///
54/// For BIP 68 relative lock-by-blocktime locks, time is measured in 512 second intervals.
55#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57pub struct Time(u16);
58
59impl Time {
60    /// Relative block time 0, can be included in any block.
61    pub const ZERO: Self = Time(0);
62
63    /// The minimum relative block time (0), can be included in any block.
64    pub const MIN: Self = Time::ZERO;
65
66    /// The maximum relative block time (33,554,432 seconds or approx 388 days).
67    pub const MAX: Self = Time(u16::MAX);
68
69    /// Creates a [`Time`] using time intervals where each interval is equivalent to 512 seconds.
70    ///
71    /// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin.
72    #[inline]
73    pub const fn from_512_second_intervals(intervals: u16) -> Self { Time(intervals) }
74
75    /// Creates a [`Time`] from seconds, converting the seconds into 512 second interval with
76    /// truncating division.
77    ///
78    /// # Errors
79    ///
80    /// Will return an error if the input cannot be encoded in 16 bits.
81    #[inline]
82    #[rustfmt::skip] // moves comments to unrelated code
83    pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
84        let interval = seconds / 512;
85        if interval <= u16::MAX as u32 { // infallible cast, needed by const code
86            Ok(Time::from_512_second_intervals(interval as u16)) // cast checked above, needed by const code
87        } else {
88            Err(TimeOverflowError { seconds })
89        }
90    }
91
92    /// Creates a [`Time`] from seconds, converting the seconds into 512 second intervals with
93    /// ceiling division.
94    ///
95    /// # Errors
96    ///
97    /// Will return an error if the input cannot be encoded in 16 bits.
98    #[inline]
99    #[rustfmt::skip] // moves comments to unrelated code
100    pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
101        if seconds <= u16::MAX as u32 * 512 {
102            let interval = (seconds + 511) / 512;
103            Ok(Time::from_512_second_intervals(interval as u16)) // cast checked above, needed by const code
104        } else {
105            Err(TimeOverflowError { seconds })
106        }
107    }
108
109    /// Returns the inner `u16` value.
110    #[inline]
111    pub const fn value(self) -> u16 { self.0 }
112
113    /// Returns the `u32` value used to encode this locktime in an nSequence field or
114    /// argument to `OP_CHECKSEQUENCEVERIFY`.
115    #[inline]
116    pub const fn to_consensus_u32(&self) -> u32 {
117        (1u32 << 22) | self.0 as u32 // cast safety: u32 is wider than u16 on all architectures
118    }
119}
120
121crate::impl_parse_str_from_int_infallible!(Time, u16, from_512_second_intervals);
122
123impl fmt::Display for Time {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
125}
126
127/// Input time in seconds was too large to be encoded to a 16 bit 512 second interval.
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct TimeOverflowError {
130    /// Time value in seconds that overflowed.
131    // Private because we maintain an invariant that the `seconds` value does actually overflow.
132    pub(crate) seconds: u32,
133}
134
135impl TimeOverflowError {
136    /// Creates a new `TimeOverflowError` using `seconds`.
137    ///
138    /// # Panics
139    ///
140    /// If `seconds` would not actually overflow a `u16`.
141    pub fn new(seconds: u32) -> Self {
142        assert!(u16::try_from((seconds + 511) / 512).is_err());
143        Self { seconds }
144    }
145}
146
147impl fmt::Display for TimeOverflowError {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        write!(
150            f,
151            "{} seconds is too large to be encoded to a 16 bit 512 second interval",
152            self.seconds
153        )
154    }
155}
156
157#[cfg(feature = "std")]
158impl std::error::Error for TimeOverflowError {}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
165
166    #[test]
167    fn from_seconds_ceil_success() {
168        let actual = Time::from_seconds_ceil(100).unwrap();
169        let expected = Time(1_u16);
170        assert_eq!(actual, expected);
171    }
172
173    #[test]
174    fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
175        let actual = Time::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
176        let expected = Time(u16::MAX);
177        assert_eq!(actual, expected);
178    }
179
180    #[test]
181    fn from_seconds_ceil_causes_time_overflow_error() {
182        let result = Time::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1);
183        assert!(result.is_err());
184    }
185
186    #[test]
187    fn from_seconds_floor_success() {
188        let actual = Time::from_seconds_floor(100).unwrap();
189        let expected = Time(0_u16);
190        assert_eq!(actual, expected);
191    }
192
193    #[test]
194    fn from_seconds_floor_with_exact_interval() {
195        let actual = Time::from_seconds_floor(512).unwrap();
196        let expected = Time(1_u16);
197        assert_eq!(actual, expected);
198    }
199
200    #[test]
201    fn from_seconds_floor_with_maximum_encodable_seconds_success() {
202        let actual = Time::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
203        let expected = Time(u16::MAX);
204        assert_eq!(actual, expected);
205    }
206
207    #[test]
208    fn from_seconds_floor_causes_time_overflow_error() {
209        let result = Time::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512);
210        assert!(result.is_err());
211    }
212}