bitcoin/blockdata/locktime/relative.rs
1// SPDX-License-Identifier: CC0-1.0
2
3//! Provides type [`LockTime`] that implements the logic around nSequence/OP_CHECKSEQUENCEVERIFY.
4//!
5//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
6//! whether bit 22 of the `u32` consensus value is set.
7//!
8
9#[cfg(feature = "ordered")]
10use core::cmp::Ordering;
11use core::{cmp, convert, fmt};
12
13#[cfg(all(test, mutate))]
14use mutagen::mutate;
15
16#[cfg(doc)]
17use crate::relative;
18use crate::Sequence;
19
20#[rustfmt::skip] // Keep public re-exports separate.
21#[doc(inline)]
22pub use units::locktime::relative::{Height, Time, TimeOverflowError};
23
24/// A relative lock time value, representing either a block height or time (512 second intervals).
25///
26/// Used for sequence numbers (`nSequence` in Bitcoin Core and [`crate::TxIn::sequence`]
27/// in this library) and also for the argument to opcode 'OP_CHECKSEQUENCEVERIFY`.
28///
29/// ### Note on ordering
30///
31/// Locktimes may be height- or time-based, and these metrics are incommensurate; there is no total
32/// ordering on locktimes. We therefore have implemented [`PartialOrd`] but not [`Ord`]. We also
33/// implement [`ordered::ArbitraryOrd`] if the "ordered" feature is enabled.
34///
35/// ### Relevant BIPs
36///
37/// * [BIP 68 Relative lock-time using consensus-enforced sequence numbers](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
38/// * [BIP 112 CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki)
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
42pub enum LockTime {
43 /// A block height lock time value.
44 Blocks(Height),
45 /// A 512 second time interval value.
46 Time(Time),
47}
48
49impl LockTime {
50 /// A relative locktime of 0 is always valid, and is assumed valid for inputs that
51 /// are not yet confirmed.
52 pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO);
53
54 /// The number of bytes that the locktime contributes to the size of a transaction.
55 pub const SIZE: usize = 4; // Serialized length of a u32.
56
57 /// Constructs a `LockTime` from an nSequence value or the argument to OP_CHECKSEQUENCEVERIFY.
58 ///
59 /// This method will **not** round-trip with [`Self::to_consensus_u32`], because relative
60 /// locktimes only use some bits of the underlying `u32` value and discard the rest. If
61 /// you want to preserve the full value, you should use the [`Sequence`] type instead.
62 ///
63 /// # Examples
64 ///
65 /// ```rust
66 /// # use bitcoin::relative::LockTime;
67 ///
68 /// // `from_consensus` roundtrips with `to_consensus_u32` for small values.
69 /// let n_lock_time: u32 = 7000;
70 /// let lock_time = LockTime::from_consensus(n_lock_time).unwrap();
71 /// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
72 /// ```
73 pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> {
74 let sequence = crate::Sequence::from_consensus(n);
75 sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
76 }
77
78 /// Returns the `u32` value used to encode this locktime in an nSequence field or
79 /// argument to `OP_CHECKSEQUENCEVERIFY`.
80 ///
81 /// # Warning
82 ///
83 /// Locktimes are not ordered by the natural ordering on `u32`. If you want to
84 /// compare locktimes, use [`Self::is_implied_by`] or similar methods.
85 #[inline]
86 pub fn to_consensus_u32(&self) -> u32 {
87 match self {
88 LockTime::Blocks(ref h) => h.to_consensus_u32(),
89 LockTime::Time(ref t) => t.to_consensus_u32(),
90 }
91 }
92
93 /// Constructs a `LockTime` from the sequence number of a Bitcoin input.
94 ///
95 /// This method will **not** round-trip with [`Self::to_sequence`]. See the
96 /// docs for [`Self::from_consensus`] for more information.
97 #[inline]
98 pub fn from_sequence(n: Sequence) -> Result<Self, DisabledLockTimeError> {
99 Self::from_consensus(n.to_consensus_u32())
100 }
101
102 /// Encodes the locktime as a sequence number.
103 #[inline]
104 pub fn to_sequence(&self) -> Sequence { Sequence::from_consensus(self.to_consensus_u32()) }
105
106 /// Constructs a `LockTime` from `n`, expecting `n` to be a 16-bit count of blocks.
107 #[inline]
108 pub const fn from_height(n: u16) -> Self { LockTime::Blocks(Height::from_height(n)) }
109
110 /// Constructs a `LockTime` from `n`, expecting `n` to be a count of 512-second intervals.
111 ///
112 /// This function is a little awkward to use, and users may wish to instead use
113 /// [`Self::from_seconds_floor`] or [`Self::from_seconds_ceil`].
114 #[inline]
115 pub const fn from_512_second_intervals(intervals: u16) -> Self {
116 LockTime::Time(Time::from_512_second_intervals(intervals))
117 }
118
119 /// Create a [`LockTime`] from seconds, converting the seconds into 512 second interval
120 /// with truncating division.
121 ///
122 /// # Errors
123 ///
124 /// Will return an error if the input cannot be encoded in 16 bits.
125 #[inline]
126 pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
127 match Time::from_seconds_floor(seconds) {
128 Ok(time) => Ok(LockTime::Time(time)),
129 Err(e) => Err(e),
130 }
131 }
132
133 /// Create a [`LockTime`] from seconds, converting the seconds into 512 second interval
134 /// with ceiling division.
135 ///
136 /// # Errors
137 ///
138 /// Will return an error if the input cannot be encoded in 16 bits.
139 #[inline]
140 pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
141 match Time::from_seconds_ceil(seconds) {
142 Ok(time) => Ok(LockTime::Time(time)),
143 Err(e) => Err(e),
144 }
145 }
146
147 /// Returns true if both lock times use the same unit i.e., both height based or both time based.
148 #[inline]
149 pub const fn is_same_unit(&self, other: LockTime) -> bool {
150 matches!(
151 (self, other),
152 (LockTime::Blocks(_), LockTime::Blocks(_)) | (LockTime::Time(_), LockTime::Time(_))
153 )
154 }
155
156 /// Returns true if this lock time value is in units of block height.
157 #[inline]
158 pub const fn is_block_height(&self) -> bool { matches!(*self, LockTime::Blocks(_)) }
159
160 /// Returns true if this lock time value is in units of time.
161 #[inline]
162 pub const fn is_block_time(&self) -> bool { !self.is_block_height() }
163
164 /// Returns true if this [`relative::LockTime`] is satisfied by either height or time.
165 ///
166 /// # Examples
167 ///
168 /// ```rust
169 /// # use bitcoin::Sequence;
170 /// # use bitcoin::locktime::relative::{LockTime, Height, Time};
171 ///
172 /// # let height = 100; // 100 blocks.
173 /// # let intervals = 70; // Approx 10 hours.
174 /// # let current_height = || Height::from(height + 10);
175 /// # let current_time = || Time::from_512_second_intervals(intervals + 10);
176 /// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
177 ///
178 /// // Users that have chain data can get the current height and time to check against a lock.
179 /// let height_and_time = (current_time(), current_height()); // tuple order does not matter.
180 /// assert!(lock.is_satisfied_by(current_height(), current_time()));
181 /// ```
182 #[inline]
183 #[cfg_attr(all(test, mutate), mutate)]
184 pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool {
185 if let Ok(true) = self.is_satisfied_by_height(h) {
186 true
187 } else {
188 matches!(self.is_satisfied_by_time(t), Ok(true))
189 }
190 }
191
192 /// Returns true if satisfaction of `other` lock time implies satisfaction of this
193 /// [`relative::LockTime`].
194 ///
195 /// A lock time can only be satisfied by n blocks being mined or n seconds passing. If you have
196 /// two lock times (same unit) then the larger lock time being satisfied implies (in a
197 /// mathematical sense) the smaller one being satisfied.
198 ///
199 /// This function is useful when checking sequence values against a lock, first one checks the
200 /// sequence represents a relative lock time by converting to `LockTime` then use this function
201 /// to see if satisfaction of the newly created lock time would imply satisfaction of `self`.
202 ///
203 /// Can also be used to remove the smaller value of two `OP_CHECKSEQUENCEVERIFY` operations
204 /// within one branch of the script.
205 ///
206 /// # Examples
207 ///
208 /// ```rust
209 /// # use bitcoin::Sequence;
210 /// # use bitcoin::locktime::relative::{LockTime, Height, Time};
211 ///
212 /// # let height = 100; // 100 blocks.
213 /// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
214 /// # let test_sequence = Sequence::from_height(height + 10);
215 ///
216 /// let satisfied = match test_sequence.to_relative_lock_time() {
217 /// None => false, // Handle non-lock-time case.
218 /// Some(test_lock) => lock.is_implied_by(test_lock),
219 /// };
220 /// assert!(satisfied);
221 /// ```
222 #[inline]
223 #[cfg_attr(all(test, mutate), mutate)]
224 pub fn is_implied_by(&self, other: LockTime) -> bool {
225 use LockTime::*;
226
227 match (*self, other) {
228 (Blocks(this), Blocks(other)) => this.value() <= other.value(),
229 (Time(this), Time(other)) => this.value() <= other.value(),
230 _ => false, // Not the same units.
231 }
232 }
233
234 /// Returns true if satisfaction of the sequence number implies satisfaction of this lock time.
235 ///
236 /// When deciding whether an instance of `<n> CHECKSEQUENCEVERIFY` will pass, this
237 /// method can be used by parsing `n` as a [`LockTime`] and calling this method
238 /// with the sequence number of the input which spends the script.
239 #[inline]
240 pub fn is_implied_by_sequence(&self, other: Sequence) -> bool {
241 if let Ok(other) = LockTime::from_sequence(other) {
242 self.is_implied_by(other)
243 } else {
244 false
245 }
246 }
247
248 /// Returns true if this [`relative::LockTime`] is satisfied by [`Height`].
249 ///
250 /// # Errors
251 ///
252 /// Returns an error if this lock is not lock-by-height.
253 ///
254 /// # Examples
255 ///
256 /// ```rust
257 /// # use bitcoin::Sequence;
258 /// # use bitcoin::locktime::relative::{LockTime, Height, Time};
259 ///
260 /// let height: u16 = 100;
261 /// let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
262 /// assert!(lock.is_satisfied_by_height(Height::from(height+1)).expect("a height"));
263 /// ```
264 #[inline]
265 #[cfg_attr(all(test, mutate), mutate)]
266 pub fn is_satisfied_by_height(&self, height: Height) -> Result<bool, IncompatibleHeightError> {
267 use LockTime::*;
268
269 match *self {
270 Blocks(ref h) => Ok(h.value() <= height.value()),
271 Time(time) => Err(IncompatibleHeightError { height, time }),
272 }
273 }
274
275 /// Returns true if this [`relative::LockTime`] is satisfied by [`Time`].
276 ///
277 /// # Errors
278 ///
279 /// Returns an error if this lock is not lock-by-time.
280 ///
281 /// # Examples
282 ///
283 /// ```rust
284 /// # use bitcoin::Sequence;
285 /// # use bitcoin::locktime::relative::{LockTime, Height, Time};
286 ///
287 /// let intervals: u16 = 70; // approx 10 hours;
288 /// let lock = Sequence::from_512_second_intervals(intervals).to_relative_lock_time().expect("valid time");
289 /// assert!(lock.is_satisfied_by_time(Time::from_512_second_intervals(intervals + 10)).expect("a time"));
290 /// ```
291 #[inline]
292 #[cfg_attr(all(test, mutate), mutate)]
293 pub fn is_satisfied_by_time(&self, time: Time) -> Result<bool, IncompatibleTimeError> {
294 use LockTime::*;
295
296 match *self {
297 Time(ref t) => Ok(t.value() <= time.value()),
298 Blocks(height) => Err(IncompatibleTimeError { time, height }),
299 }
300 }
301}
302
303impl From<Height> for LockTime {
304 #[inline]
305 fn from(h: Height) -> Self { LockTime::Blocks(h) }
306}
307
308impl From<Time> for LockTime {
309 #[inline]
310 fn from(t: Time) -> Self { LockTime::Time(t) }
311}
312
313impl PartialOrd for LockTime {
314 #[inline]
315 fn partial_cmp(&self, other: &LockTime) -> Option<cmp::Ordering> {
316 use LockTime::*;
317
318 match (*self, *other) {
319 (Blocks(ref a), Blocks(ref b)) => a.partial_cmp(b),
320 (Time(ref a), Time(ref b)) => a.partial_cmp(b),
321 (_, _) => None,
322 }
323 }
324}
325
326impl fmt::Display for LockTime {
327 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
328 use LockTime::*;
329
330 if f.alternate() {
331 match *self {
332 Blocks(ref h) => write!(f, "block-height {}", h),
333 Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
334 }
335 } else {
336 match *self {
337 Blocks(ref h) => fmt::Display::fmt(h, f),
338 Time(ref t) => fmt::Display::fmt(t, f),
339 }
340 }
341 }
342}
343
344#[cfg(feature = "ordered")]
345impl ordered::ArbitraryOrd for LockTime {
346 fn arbitrary_cmp(&self, other: &Self) -> Ordering {
347 use LockTime::*;
348
349 match (self, other) {
350 (Blocks(_), Time(_)) => Ordering::Less,
351 (Time(_), Blocks(_)) => Ordering::Greater,
352 (Blocks(this), Blocks(that)) => this.cmp(that),
353 (Time(this), Time(that)) => this.cmp(that),
354 }
355 }
356}
357
358impl convert::TryFrom<Sequence> for LockTime {
359 type Error = DisabledLockTimeError;
360 fn try_from(seq: Sequence) -> Result<LockTime, DisabledLockTimeError> {
361 LockTime::from_sequence(seq)
362 }
363}
364
365impl From<LockTime> for Sequence {
366 fn from(lt: LockTime) -> Sequence { lt.to_sequence() }
367}
368
369/// Error returned when a sequence number is parsed as a lock time, but its
370/// "disable" flag is set.
371#[derive(Debug, Clone, Eq, PartialEq)]
372pub struct DisabledLockTimeError(u32);
373
374impl DisabledLockTimeError {
375 /// Accessor for the `u32` whose "disable" flag was set, preventing
376 /// it from being parsed as a relative locktime.
377 pub fn disabled_locktime_value(&self) -> u32 { self.0 }
378}
379
380impl fmt::Display for DisabledLockTimeError {
381 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382 write!(f, "lock time 0x{:08x} has disable flag set", self.0)
383 }
384}
385
386#[cfg(feature = "std")]
387impl std::error::Error for DisabledLockTimeError {}
388
389/// Tried to satisfy a lock-by-blocktime lock using a height value.
390#[derive(Debug, Clone, PartialEq, Eq)]
391#[non_exhaustive]
392pub struct IncompatibleHeightError {
393 /// Attempted to satisfy a lock-by-blocktime lock with this height.
394 pub height: Height,
395 /// The inner time value of the lock-by-blocktime lock.
396 pub time: Time,
397}
398
399impl fmt::Display for IncompatibleHeightError {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 write!(
402 f,
403 "tried to satisfy a lock-by-blocktime lock {} with height: {}",
404 self.time, self.height
405 )
406 }
407}
408
409#[cfg(feature = "std")]
410impl std::error::Error for IncompatibleHeightError {}
411
412/// Tried to satisfy a lock-by-blockheight lock using a time value.
413#[derive(Debug, Clone, PartialEq, Eq)]
414#[non_exhaustive]
415pub struct IncompatibleTimeError {
416 /// Attempted to satisfy a lock-by-blockheight lock with this time.
417 pub time: Time,
418 /// The inner height value of the lock-by-blockheight lock.
419 pub height: Height,
420}
421
422impl fmt::Display for IncompatibleTimeError {
423 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424 write!(
425 f,
426 "tried to satisfy a lock-by-blockheight lock {} with time: {}",
427 self.height, self.time
428 )
429 }
430}
431
432#[cfg(feature = "std")]
433impl std::error::Error for IncompatibleTimeError {}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn satisfied_by_height() {
441 let height = Height::from(10);
442 let time = Time::from_512_second_intervals(70);
443
444 let lock = LockTime::from(height);
445
446 assert!(!lock.is_satisfied_by(Height::from(9), time));
447 assert!(lock.is_satisfied_by(Height::from(10), time));
448 assert!(lock.is_satisfied_by(Height::from(11), time));
449 }
450
451 #[test]
452 fn satisfied_by_time() {
453 let height = Height::from(10);
454 let time = Time::from_512_second_intervals(70);
455
456 let lock = LockTime::from(time);
457
458 assert!(!lock.is_satisfied_by(height, Time::from_512_second_intervals(69)));
459 assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(70)));
460 assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(71)));
461 }
462
463 #[test]
464 fn height_correctly_implies() {
465 let height = Height::from(10);
466 let lock = LockTime::from(height);
467
468 assert!(!lock.is_implied_by(LockTime::from(Height::from(9))));
469 assert!(lock.is_implied_by(LockTime::from(Height::from(10))));
470 assert!(lock.is_implied_by(LockTime::from(Height::from(11))));
471 }
472
473 #[test]
474 fn time_correctly_implies() {
475 let time = Time::from_512_second_intervals(70);
476 let lock = LockTime::from(time);
477
478 assert!(!lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(69))));
479 assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(70))));
480 assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(71))));
481 }
482
483 #[test]
484 fn incorrect_units_do_not_imply() {
485 let time = Time::from_512_second_intervals(70);
486 let height = Height::from(10);
487
488 let lock = LockTime::from(time);
489 assert!(!lock.is_implied_by(LockTime::from(height)));
490 }
491
492 #[test]
493 fn consensus_round_trip() {
494 assert!(LockTime::from_consensus(1 << 31).is_err());
495 assert!(LockTime::from_consensus(1 << 30).is_ok());
496 // Relative locktimes do not care about bits 17 through 21.
497 assert_eq!(LockTime::from_consensus(65536), LockTime::from_consensus(0));
498
499 for val in [0u32, 1, 1000, 65535] {
500 let seq = Sequence::from_consensus(val);
501 let lt = LockTime::from_consensus(val).unwrap();
502 assert_eq!(lt.to_consensus_u32(), val);
503 assert_eq!(lt.to_sequence(), seq);
504 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
505
506 let seq = Sequence::from_consensus(val + (1 << 22));
507 let lt = LockTime::from_consensus(val + (1 << 22)).unwrap();
508 assert_eq!(lt.to_consensus_u32(), val + (1 << 22));
509 assert_eq!(lt.to_sequence(), seq);
510 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
511 }
512 }
513}