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