bitcoin/blockdata/locktime/
absolute.rs1use core::cmp::Ordering;
10use core::fmt;
11use core::str::FromStr;
12
13use io::{Read, Write};
14#[cfg(all(test, mutate))]
15use mutagen::mutate;
16use units::parse::{self, ParseIntError};
17
18#[cfg(doc)]
19use crate::absolute;
20use crate::consensus::encode::{self, Decodable, Encodable};
21use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
22use crate::prelude::{Box, String};
23
24#[rustfmt::skip] #[doc(inline)]
26pub use units::locktime::absolute::{
27 Height, Time, LOCK_TIME_THRESHOLD, ConversionError, ParseHeightError, ParseTimeError,
28};
29
30#[derive(Clone, Copy, PartialEq, Eq, Hash)]
63pub enum LockTime {
64 Blocks(Height),
76 Seconds(Time),
88}
89
90impl LockTime {
91 pub const ZERO: LockTime = LockTime::Blocks(Height::ZERO);
94
95 pub const SIZE: usize = 4; pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
100 let stripped = if let Some(stripped) = s.strip_prefix("0x") {
101 stripped
102 } else if let Some(stripped) = s.strip_prefix("0X") {
103 stripped
104 } else {
105 return Err(MissingPrefixError::new(s).into());
106 };
107
108 let lock_time = parse::hex_u32(stripped)?;
109 Ok(Self::from_consensus(lock_time))
110 }
111
112 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
114 if s.starts_with("0x") || s.starts_with("0X") {
115 return Err(ContainsPrefixError::new(s).into());
116 }
117 let lock_time = parse::hex_u32(s)?;
118 Ok(Self::from_consensus(lock_time))
119 }
120
121 #[inline]
134 pub fn from_consensus(n: u32) -> Self {
135 if units::locktime::absolute::is_block_height(n) {
136 Self::Blocks(Height::from_consensus(n).expect("n is valid"))
137 } else {
138 Self::Seconds(Time::from_consensus(n).expect("n is valid"))
139 }
140 }
141
142 #[inline]
153 pub fn from_height(n: u32) -> Result<Self, ConversionError> {
154 let height = Height::from_consensus(n)?;
155 Ok(LockTime::Blocks(height))
156 }
157
158 #[inline]
169 pub fn from_time(n: u32) -> Result<Self, ConversionError> {
170 let time = Time::from_consensus(n)?;
171 Ok(LockTime::Seconds(time))
172 }
173
174 #[inline]
176 pub const fn is_same_unit(&self, other: LockTime) -> bool {
177 matches!(
178 (self, other),
179 (LockTime::Blocks(_), LockTime::Blocks(_))
180 | (LockTime::Seconds(_), LockTime::Seconds(_))
181 )
182 }
183
184 #[inline]
186 pub const fn is_block_height(&self) -> bool { matches!(*self, LockTime::Blocks(_)) }
187
188 #[inline]
190 pub const fn is_block_time(&self) -> bool { !self.is_block_height() }
191
192 #[inline]
214 #[cfg_attr(all(test, mutate), mutate)]
215 pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
216 use LockTime::*;
217
218 match *self {
219 Blocks(n) => n <= height,
220 Seconds(n) => n <= time,
221 }
222 }
223
224 #[inline]
244 #[cfg_attr(all(test, mutate), mutate)]
245 pub fn is_implied_by(&self, other: LockTime) -> bool {
246 use LockTime::*;
247
248 match (*self, other) {
249 (Blocks(this), Blocks(other)) => this <= other,
250 (Seconds(this), Seconds(other)) => this <= other,
251 _ => false, }
253 }
254
255 #[inline]
281 pub fn to_consensus_u32(self) -> u32 {
282 match self {
283 LockTime::Blocks(ref h) => h.to_consensus_u32(),
284 LockTime::Seconds(ref t) => t.to_consensus_u32(),
285 }
286 }
287}
288
289impl FromStr for LockTime {
290 type Err = ParseIntError;
291
292 fn from_str(s: &str) -> Result<Self, Self::Err> {
293 parse::int::<u32, &str>(s).map(LockTime::from_consensus)
294 }
295}
296
297impl TryFrom<&str> for LockTime {
298 type Error = ParseIntError;
299
300 fn try_from(s: &str) -> Result<Self, Self::Error> {
301 LockTime::from_str(s)
302 }
303}
304
305impl TryFrom<String> for LockTime {
306 type Error = ParseIntError;
307
308 fn try_from(s: String) -> Result<Self, Self::Error> {
309 LockTime::from_str(&s)
310 }
311}
312
313impl TryFrom<Box<str>> for LockTime {
314 type Error = ParseIntError;
315
316 fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
317 LockTime::from_str(&s)
318 }
319}
320
321impl From<Height> for LockTime {
322 #[inline]
323 fn from(h: Height) -> Self { LockTime::Blocks(h) }
324}
325
326impl From<Time> for LockTime {
327 #[inline]
328 fn from(t: Time) -> Self { LockTime::Seconds(t) }
329}
330
331impl PartialOrd for LockTime {
332 #[inline]
333 fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
334 use LockTime::*;
335
336 match (*self, *other) {
337 (Blocks(ref a), Blocks(ref b)) => a.partial_cmp(b),
338 (Seconds(ref a), Seconds(ref b)) => a.partial_cmp(b),
339 (_, _) => None,
340 }
341 }
342}
343
344impl fmt::Debug for LockTime {
345 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346 use LockTime::*;
347
348 match *self {
349 Blocks(ref h) => write!(f, "{} blocks", h),
350 Seconds(ref t) => write!(f, "{} seconds", t),
351 }
352 }
353}
354
355impl fmt::Display for LockTime {
356 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357 use LockTime::*;
358
359 if f.alternate() {
360 match *self {
361 Blocks(ref h) => write!(f, "block-height {}", h),
362 Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
363 }
364 } else {
365 match *self {
366 Blocks(ref h) => fmt::Display::fmt(h, f),
367 Seconds(ref t) => fmt::Display::fmt(t, f),
368 }
369 }
370 }
371}
372
373impl Encodable for LockTime {
374 #[inline]
375 fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
376 let v = self.to_consensus_u32();
377 v.consensus_encode(w)
378 }
379}
380
381impl Decodable for LockTime {
382 #[inline]
383 fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
384 u32::consensus_decode(r).map(LockTime::from_consensus)
385 }
386}
387
388#[cfg(feature = "serde")]
389impl serde::Serialize for LockTime {
390 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
391 where
392 S: serde::Serializer,
393 {
394 serializer.serialize_u32(self.to_consensus_u32())
395 }
396}
397
398#[cfg(feature = "serde")]
399impl<'de> serde::Deserialize<'de> for LockTime {
400 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
401 where
402 D: serde::Deserializer<'de>,
403 {
404 struct Visitor;
405 impl<'de> serde::de::Visitor<'de> for Visitor {
406 type Value = u32;
407 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("a u32") }
408 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<u32, E> {
412 v.try_into().map_err(|_| {
413 E::invalid_value(serde::de::Unexpected::Unsigned(v), &"a 32-bit number")
414 })
415 }
416 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<u32, E> {
418 v.try_into().map_err(|_| {
419 E::invalid_value(serde::de::Unexpected::Signed(v), &"a 32-bit number")
420 })
421 }
422 }
423 deserializer.deserialize_u32(Visitor).map(LockTime::from_consensus)
424 }
425}
426
427#[cfg(feature = "ordered")]
428impl ordered::ArbitraryOrd for LockTime {
429 fn arbitrary_cmp(&self, other: &Self) -> Ordering {
430 use LockTime::*;
431
432 match (self, other) {
433 (Blocks(_), Seconds(_)) => Ordering::Less,
434 (Seconds(_), Blocks(_)) => Ordering::Greater,
435 (Blocks(this), Blocks(that)) => this.cmp(that),
436 (Seconds(this), Seconds(that)) => this.cmp(that),
437 }
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn display_and_alternate() {
447 let n = LockTime::from_consensus(741521);
448 let s = format!("{}", n);
449 assert_eq!(&s, "741521");
450
451 let got = format!("{:#}", n);
452 assert_eq!(got, "block-height 741521");
453 }
454
455 #[test]
456 fn lock_time_from_hex_lower() {
457 let lock = LockTime::from_hex("0x6289c350").unwrap();
458 assert_eq!(lock, LockTime::from_consensus(0x6289C350));
459 }
460
461 #[test]
462 fn lock_time_from_hex_upper() {
463 let lock = LockTime::from_hex("0X6289C350").unwrap();
464 assert_eq!(lock, LockTime::from_consensus(0x6289C350));
465 }
466
467 #[test]
468 fn lock_time_from_unprefixed_hex_lower() {
469 let lock = LockTime::from_unprefixed_hex("6289c350").unwrap();
470 assert_eq!(lock, LockTime::from_consensus(0x6289C350));
471 }
472
473 #[test]
474 fn lock_time_from_unprefixed_hex_upper() {
475 let lock = LockTime::from_unprefixed_hex("6289C350").unwrap();
476 assert_eq!(lock, LockTime::from_consensus(0x6289C350));
477 }
478
479 #[test]
480 fn lock_time_from_invalid_hex_should_err() {
481 let hex = "0xzb93";
482 let result = LockTime::from_hex(hex);
483 assert!(result.is_err());
484 }
485
486 #[test]
487 fn parses_correctly_to_height_or_time() {
488 let lock = LockTime::from_consensus(750_000);
489
490 assert!(lock.is_block_height());
491 assert!(!lock.is_block_time());
492
493 let t: u32 = 1653195600; let lock = LockTime::from_consensus(t);
495
496 assert!(!lock.is_block_height());
497 assert!(lock.is_block_time());
498 }
499
500 #[test]
501 fn satisfied_by_height() {
502 let lock = LockTime::from_consensus(750_000);
503
504 let height = Height::from_consensus(800_000).expect("failed to parse height");
505
506 let t: u32 = 1653195600; let time = Time::from_consensus(t).expect("invalid time value");
508
509 assert!(lock.is_satisfied_by(height, time))
510 }
511
512 #[test]
513 fn satisfied_by_time() {
514 let lock = LockTime::from_consensus(1053195600);
515
516 let t: u32 = 1653195600; let time = Time::from_consensus(t).expect("invalid time value");
518
519 let height = Height::from_consensus(800_000).expect("failed to parse height");
520
521 assert!(lock.is_satisfied_by(height, time))
522 }
523
524 #[test]
525 fn satisfied_by_same_height() {
526 let h = 750_000;
527 let lock = LockTime::from_consensus(h);
528 let height = Height::from_consensus(h).expect("failed to parse height");
529
530 let t: u32 = 1653195600; let time = Time::from_consensus(t).expect("invalid time value");
532
533 assert!(lock.is_satisfied_by(height, time))
534 }
535
536 #[test]
537 fn satisfied_by_same_time() {
538 let t: u32 = 1653195600; let lock = LockTime::from_consensus(t);
540 let time = Time::from_consensus(t).expect("invalid time value");
541
542 let height = Height::from_consensus(800_000).expect("failed to parse height");
543
544 assert!(lock.is_satisfied_by(height, time))
545 }
546
547 #[test]
548 fn height_correctly_implies() {
549 let lock = LockTime::from_consensus(750_005);
550
551 assert!(!lock.is_implied_by(LockTime::from_consensus(750_004)));
552 assert!(lock.is_implied_by(LockTime::from_consensus(750_005)));
553 assert!(lock.is_implied_by(LockTime::from_consensus(750_006)));
554 }
555
556 #[test]
557 fn time_correctly_implies() {
558 let t: u32 = 1700000005;
559 let lock = LockTime::from_consensus(t);
560
561 assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
562 assert!(lock.is_implied_by(LockTime::from_consensus(1700000005)));
563 assert!(lock.is_implied_by(LockTime::from_consensus(1700000006)));
564 }
565
566 #[test]
567 fn incorrect_units_do_not_imply() {
568 let lock = LockTime::from_consensus(750_005);
569 assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
570 }
571}