1pub mod error;
10
11use core::{convert, fmt};
12
13#[cfg(feature = "arbitrary")]
14use arbitrary::{Arbitrary, Unstructured};
15use internals::const_casts;
16
17use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
18#[cfg(doc)]
19use crate::relative;
20use crate::{BlockHeight, BlockMtp, Sequence};
21
22#[rustfmt::skip] #[doc(no_inline)]
24pub use self::error::{
25 DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, InvalidHeightError,
26 InvalidTimeError, IsSatisfiedByError, IsSatisfiedByHeightError, IsSatisfiedByTimeError,
27 TimeOverflowError,
28};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum LockTime {
47 Blocks(NumberOfBlocks),
49 Time(NumberOf512Seconds),
51}
52
53impl LockTime {
54 pub const ZERO: Self = Self::Blocks(NumberOfBlocks::ZERO);
57
58 pub const SIZE: usize = 4; #[inline]
91 pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> {
92 let sequence = crate::Sequence::from_consensus(n);
93 sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
94 }
95
96 #[inline]
104 pub fn to_consensus_u32(self) -> u32 {
105 match self {
106 Self::Blocks(ref h) => u32::from(h.to_height()),
107 Self::Time(ref t) => Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()),
108 }
109 }
110
111 #[inline]
133 pub fn from_sequence(n: Sequence) -> Result<Self, DisabledLockTimeError> {
134 Self::from_consensus(n.to_consensus_u32())
135 }
136
137 #[inline]
139 pub fn to_sequence(self) -> Sequence { Sequence::from_consensus(self.to_consensus_u32()) }
140
141 #[inline]
143 pub const fn from_height(n: u16) -> Self { Self::Blocks(NumberOfBlocks::from_height(n)) }
144
145 #[inline]
150 pub const fn from_512_second_intervals(intervals: u16) -> Self {
151 Self::Time(NumberOf512Seconds::from_512_second_intervals(intervals))
152 }
153
154 #[inline]
161 pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
162 match NumberOf512Seconds::from_seconds_floor(seconds) {
163 Ok(time) => Ok(Self::Time(time)),
164 Err(e) => Err(e),
165 }
166 }
167
168 #[inline]
175 pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
176 match NumberOf512Seconds::from_seconds_ceil(seconds) {
177 Ok(time) => Ok(Self::Time(time)),
178 Err(e) => Err(e),
179 }
180 }
181
182 #[inline]
184 pub const fn is_same_unit(self, other: Self) -> bool {
185 matches!((self, other), (Self::Blocks(_), Self::Blocks(_)) | (Self::Time(_), Self::Time(_)))
186 }
187
188 #[inline]
190 pub const fn is_block_height(self) -> bool { matches!(self, Self::Blocks(_)) }
191
192 #[inline]
194 pub const fn is_block_time(self) -> bool { !self.is_block_height() }
195
196 #[inline]
205 pub fn is_satisfied_by(
206 self,
207 chain_tip_height: BlockHeight,
208 chain_tip_mtp: BlockMtp,
209 utxo_mined_at_height: BlockHeight,
210 utxo_mined_at_mtp: BlockMtp,
211 ) -> Result<bool, IsSatisfiedByError> {
212 match self {
213 Self::Blocks(blocks) => blocks
214 .is_satisfied_by(chain_tip_height, utxo_mined_at_height)
215 .map_err(IsSatisfiedByError::Blocks),
216 Self::Time(time) => time
217 .is_satisfied_by(chain_tip_mtp, utxo_mined_at_mtp)
218 .map_err(IsSatisfiedByError::Time),
219 }
220 }
221
222 #[inline]
231 pub fn is_satisfied_by_height(
232 self,
233 chain_tip: BlockHeight,
234 utxo_mined_at: BlockHeight,
235 ) -> Result<bool, IsSatisfiedByHeightError> {
236 match self {
237 Self::Blocks(blocks) => blocks
238 .is_satisfied_by(chain_tip, utxo_mined_at)
239 .map_err(IsSatisfiedByHeightError::Satisfaction),
240 Self::Time(time) =>
241 Err(IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(time))),
242 }
243 }
244
245 #[inline]
254 pub fn is_satisfied_by_time(
255 self,
256 chain_tip: BlockMtp,
257 utxo_mined_at: BlockMtp,
258 ) -> Result<bool, IsSatisfiedByTimeError> {
259 match self {
260 Self::Time(time) => time
261 .is_satisfied_by(chain_tip, utxo_mined_at)
262 .map_err(IsSatisfiedByTimeError::Satisfaction),
263 Self::Blocks(blocks) =>
264 Err(IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(blocks))),
265 }
266 }
267
268 #[inline]
298 pub fn is_implied_by(self, other: Self) -> bool {
299 match (self, other) {
300 (Self::Blocks(this), Self::Blocks(other)) => this <= other,
301 (Self::Time(this), Self::Time(other)) => this <= other,
302 _ => false, }
304 }
305
306 #[inline]
327 pub fn is_implied_by_sequence(self, other: Sequence) -> bool {
328 Self::from_sequence(other).is_ok_and(|other| self.is_implied_by(other))
329 }
330}
331
332impl From<NumberOfBlocks> for LockTime {
333 #[inline]
334 fn from(h: NumberOfBlocks) -> Self { Self::Blocks(h) }
335}
336
337impl From<NumberOf512Seconds> for LockTime {
338 #[inline]
339 fn from(t: NumberOf512Seconds) -> Self { Self::Time(t) }
340}
341
342impl fmt::Display for LockTime {
343 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344 if f.alternate() {
345 match *self {
346 Self::Blocks(ref h) => write!(f, "block-height {}", h),
347 Self::Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
348 }
349 } else {
350 match *self {
351 Self::Blocks(ref h) => fmt::Display::fmt(h, f),
352 Self::Time(ref t) => fmt::Display::fmt(t, f),
353 }
354 }
355 }
356}
357
358impl convert::TryFrom<Sequence> for LockTime {
359 type Error = DisabledLockTimeError;
360 #[inline]
361 fn try_from(seq: Sequence) -> Result<Self, DisabledLockTimeError> { Self::from_sequence(seq) }
362}
363
364impl From<LockTime> for Sequence {
365 #[inline]
366 fn from(lt: LockTime) -> Self { lt.to_sequence() }
367}
368
369#[cfg(feature = "serde")]
370impl serde::Serialize for LockTime {
371 #[inline]
372 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
373 where
374 S: serde::Serializer,
375 {
376 self.to_consensus_u32().serialize(serializer)
377 }
378}
379
380#[cfg(feature = "serde")]
381impl<'de> serde::Deserialize<'de> for LockTime {
382 #[inline]
383 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
384 where
385 D: serde::Deserializer<'de>,
386 {
387 u32::deserialize(deserializer)
388 .and_then(|n| Self::from_consensus(n).map_err(serde::de::Error::custom))
389 }
390}
391
392#[deprecated(since = "1.0.0-rc.0", note = "use `NumberOfBlocks` instead")]
393#[doc(hidden)]
394pub type Height = NumberOfBlocks;
395
396#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
398pub struct NumberOfBlocks(u16);
399
400impl NumberOfBlocks {
401 pub const ZERO: Self = Self(0);
403
404 pub const MIN: Self = Self::ZERO;
406
407 pub const MAX: Self = Self(u16::MAX);
409
410 #[inline]
412 pub const fn from_height(blocks: u16) -> Self { Self(blocks) }
413
414 #[inline]
416 #[must_use]
417 pub const fn to_height(self) -> u16 { self.0 }
418
419 #[inline]
421 #[must_use]
422 #[deprecated(since = "1.0.0-rc.0", note = "use `to_height` instead")]
423 #[doc(hidden)]
424 pub const fn value(self) -> u16 { self.0 }
425
426 #[deprecated(
429 since = "1.0.0-rc.0",
430 note = "use `LockTime::from` followed by `to_consensus_u32` instead"
431 )]
432 pub const fn to_consensus_u32(self) -> u32 {
433 self.0 as u32 }
435
436 #[inline]
443 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
444 let block_count = parse_int::hex_u16_prefixed(s)?;
445 Ok(Self::from_height(block_count))
446 }
447
448 #[inline]
455 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
456 let block_count = parse_int::hex_u16_unprefixed(s)?;
457 Ok(Self::from_height(block_count))
458 }
459
460 pub fn is_satisfied_by(
466 self,
467 chain_tip: crate::BlockHeight,
468 utxo_mined_at: crate::BlockHeight,
469 ) -> Result<bool, InvalidHeightError> {
470 match chain_tip.checked_sub(utxo_mined_at) {
471 Some(diff) => {
472 if diff.to_u32() == u32::MAX {
473 return Ok(true);
475 }
476 Ok(u32::from(self.to_height()) <= diff.to_u32() + 1)
478 }
479 None => Err(InvalidHeightError { chain_tip, utxo_mined_at }),
480 }
481 }
482}
483
484crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOfBlocks);
485
486impl From<u16> for NumberOfBlocks {
487 #[inline]
488 fn from(value: u16) -> Self { Self(value) }
489}
490
491parse_int::impl_parse_str_from_int_infallible!(NumberOfBlocks, u16, from);
492
493impl fmt::Display for NumberOfBlocks {
494 #[inline]
495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
496}
497
498#[deprecated(since = "1.0.0-rc.0", note = "use `NumberOf512Seconds` instead")]
499#[doc(hidden)]
500pub type Time = NumberOf512Seconds;
501
502#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
506pub struct NumberOf512Seconds(u16);
507
508impl NumberOf512Seconds {
509 pub const ZERO: Self = Self(0);
511
512 pub const MIN: Self = Self::ZERO;
514
515 pub const MAX: Self = Self(u16::MAX);
517
518 #[inline]
523 pub const fn from_512_second_intervals(intervals: u16) -> Self { Self(intervals) }
524
525 #[inline]
527 #[must_use]
528 pub const fn to_512_second_intervals(self) -> u16 { self.0 }
529
530 #[inline]
537 #[rustfmt::skip] pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
539 let interval = seconds / 512;
540 if interval <= u16::MAX as u32 { Ok(Self::from_512_second_intervals(interval as u16)) } else {
543 Err(TimeOverflowError { seconds })
544 }
545 }
546
547 #[inline]
554 #[rustfmt::skip] pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
556 if seconds <= u16::MAX as u32 * 512 {
557 let interval = seconds.div_ceil(512);
558 Ok(Self::from_512_second_intervals(interval as u16)) } else {
560 Err(TimeOverflowError { seconds })
561 }
562 }
563
564 #[inline]
566 pub const fn to_seconds(self) -> u32 { const_casts::u16_to_u32(self.0) * 512 }
567
568 #[inline]
570 #[must_use]
571 #[deprecated(since = "1.0.0-rc.0", note = "use `to_512_second_intervals` instead")]
572 #[doc(hidden)]
573 pub const fn value(self) -> u16 { self.0 }
574
575 #[deprecated(
578 since = "1.0.0-rc.0",
579 note = "use `LockTime::from` followed by `to_consensus_u32` instead"
580 )]
581 pub const fn to_consensus_u32(self) -> u32 {
582 (1u32 << 22) | self.0 as u32 }
584
585 #[inline]
592 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
593 let block_count = parse_int::hex_u16_prefixed(s)?;
594 Ok(Self::from_512_second_intervals(block_count))
595 }
596
597 #[inline]
604 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
605 let block_count = parse_int::hex_u16_unprefixed(s)?;
606 Ok(Self::from_512_second_intervals(block_count))
607 }
608
609 pub fn is_satisfied_by(
615 self,
616 chain_tip: crate::BlockMtp,
617 utxo_mined_at: crate::BlockMtp,
618 ) -> Result<bool, InvalidTimeError> {
619 chain_tip
620 .checked_sub(utxo_mined_at)
621 .ok_or(InvalidTimeError { chain_tip, utxo_mined_at })
622 .map(|diff| self.to_seconds() <= diff.to_u32())
623 }
624}
625
626crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOf512Seconds);
627
628parse_int::impl_parse_str_from_int_infallible!(NumberOf512Seconds, u16, from_512_second_intervals);
629
630impl fmt::Display for NumberOf512Seconds {
631 #[inline]
632 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
633}
634
635#[cfg(feature = "arbitrary")]
636impl<'a> Arbitrary<'a> for LockTime {
637 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
638 let choice = u.int_in_range(0..=1)?;
639
640 match choice {
641 0 => Ok(Self::Blocks(NumberOfBlocks::arbitrary(u)?)),
642 _ => Ok(Self::Time(NumberOf512Seconds::arbitrary(u)?)),
643 }
644 }
645}
646
647#[cfg(feature = "arbitrary")]
648impl<'a> Arbitrary<'a> for NumberOfBlocks {
649 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
650 let choice = u.int_in_range(0..=2)?;
651
652 match choice {
653 0 => Ok(Self::MIN),
654 1 => Ok(Self::MAX),
655 _ => Ok(Self::from_height(u16::arbitrary(u)?)),
656 }
657 }
658}
659
660#[cfg(feature = "arbitrary")]
661impl<'a> Arbitrary<'a> for NumberOf512Seconds {
662 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
663 let choice = u.int_in_range(0..=2)?;
664
665 match choice {
666 0 => Ok(Self::MIN),
667 1 => Ok(Self::MAX),
668 _ => Ok(Self::from_512_second_intervals(u16::arbitrary(u)?)),
669 }
670 }
671}
672
673#[cfg(test)]
674mod tests {
675 #[cfg(feature = "alloc")]
676 use alloc::format;
677
678 use super::*;
679 use crate::{BlockHeight, BlockTime};
680
681 const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
682
683 #[test]
684 #[cfg(feature = "alloc")]
685 fn display_and_alternate() {
686 let lock_by_height = LockTime::from_height(10);
687 let lock_by_time = LockTime::from_512_second_intervals(70);
688
689 assert_eq!(format!("{}", lock_by_height), "10");
690 assert_eq!(format!("{:#}", lock_by_height), "block-height 10");
691 assert!(!format!("{:?}", lock_by_height).is_empty());
692
693 assert_eq!(format!("{}", lock_by_time), "70");
694 assert_eq!(format!("{:#}", lock_by_time), "block-time 70 (512 second intervals)");
695 assert!(!format!("{:?}", lock_by_time).is_empty());
696 }
697
698 #[test]
699 fn from_seconds_ceil_and_floor() {
700 let time = 70 * 512 + 1;
701 let lock_by_time = LockTime::from_seconds_ceil(time).unwrap();
702 assert_eq!(lock_by_time, LockTime::from_512_second_intervals(71));
703
704 let lock_by_time = LockTime::from_seconds_floor(time).unwrap();
705 assert_eq!(lock_by_time, LockTime::from_512_second_intervals(70));
706
707 let mut max_time = 0xffff * 512;
708 assert_eq!(LockTime::from_seconds_ceil(max_time), LockTime::from_seconds_floor(max_time));
709 max_time += 512;
710 assert!(LockTime::from_seconds_ceil(max_time).is_err());
711 assert!(LockTime::from_seconds_floor(max_time).is_err());
712 }
713
714 #[test]
715 fn parses_correctly_to_height_or_time() {
716 let height1 = NumberOfBlocks::from(10);
717 let height2 = NumberOfBlocks::from(11);
718 let time1 = NumberOf512Seconds::from_512_second_intervals(70);
719 let time2 = NumberOf512Seconds::from_512_second_intervals(71);
720
721 let lock_by_height1 = LockTime::from(height1);
722 let lock_by_height2 = LockTime::from(height2);
723 let lock_by_time1 = LockTime::from(time1);
724 let lock_by_time2 = LockTime::from(time2);
725
726 assert!(lock_by_height1.is_block_height());
727 assert!(!lock_by_height1.is_block_time());
728
729 assert!(!lock_by_time1.is_block_height());
730 assert!(lock_by_time1.is_block_time());
731
732 assert!(lock_by_height1.is_same_unit(lock_by_height2));
734 assert!(!lock_by_height1.is_same_unit(lock_by_time1));
735 assert!(lock_by_time1.is_same_unit(lock_by_time2));
736 assert!(!lock_by_time1.is_same_unit(lock_by_height1));
737 }
738
739 #[test]
740 fn height_correctly_implies() {
741 let height = NumberOfBlocks::from(10);
742 let lock_by_height = LockTime::from(height);
743
744 assert!(!lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from(9))));
745 assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from(10))));
746 assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from(11))));
747 }
748
749 #[test]
750 fn time_correctly_implies() {
751 let time = NumberOf512Seconds::from_512_second_intervals(70);
752 let lock_by_time = LockTime::from(time);
753
754 assert!(!lock_by_time
755 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(69))));
756 assert!(lock_by_time
757 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(70))));
758 assert!(lock_by_time
759 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(71))));
760 }
761
762 #[test]
763 fn sequence_correctly_implies() {
764 let height = NumberOfBlocks::from(10);
765 let time = NumberOf512Seconds::from_512_second_intervals(70);
766
767 let lock_by_height = LockTime::from(height);
768 let lock_by_time = LockTime::from(time);
769
770 let seq_height = Sequence::from(lock_by_height);
771 let seq_time = Sequence::from(lock_by_time);
772
773 assert!(lock_by_height.is_implied_by_sequence(seq_height));
774 assert!(!lock_by_height.is_implied_by_sequence(seq_time));
775
776 assert!(lock_by_time.is_implied_by_sequence(seq_time));
777 assert!(!lock_by_time.is_implied_by_sequence(seq_height));
778
779 let disabled_sequence = Sequence::from_consensus(1 << 31);
780 assert!(!lock_by_height.is_implied_by_sequence(disabled_sequence));
781 assert!(!lock_by_time.is_implied_by_sequence(disabled_sequence));
782 }
783
784 #[test]
785 fn incorrect_units_do_not_imply() {
786 let time = NumberOf512Seconds::from_512_second_intervals(70);
787 let height = NumberOfBlocks::from(10);
788
789 let lock_by_time = LockTime::from(time);
790 assert!(!lock_by_time.is_implied_by(LockTime::from(height)));
791 }
792
793 #[test]
794 fn consensus_round_trip() {
795 assert!(LockTime::from_consensus(1 << 31).is_err());
796 assert!(LockTime::from_consensus(1 << 30).is_ok());
797 assert_eq!(LockTime::from_consensus(65536), LockTime::from_consensus(0));
799
800 for val in [0u32, 1, 1000, 65535] {
801 let seq = Sequence::from_consensus(val);
802 let lt = LockTime::from_consensus(val).unwrap();
803 assert_eq!(lt.to_consensus_u32(), val);
804 assert_eq!(lt.to_sequence(), seq);
805 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
806
807 let seq = Sequence::from_consensus(val + (1 << 22));
808 let lt = LockTime::from_consensus(val + (1 << 22)).unwrap();
809 assert_eq!(lt.to_consensus_u32(), val + (1 << 22));
810 assert_eq!(lt.to_sequence(), seq);
811 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
812 }
813 }
814
815 #[test]
816 #[cfg(feature = "alloc")]
817 fn disabled_locktime_error() {
818 let disabled_sequence = Sequence::from_consensus(1 << 31);
819 let err = LockTime::try_from(disabled_sequence).unwrap_err();
820
821 assert_eq!(err.disabled_locktime_value(), 1 << 31);
822 assert!(!format!("{}", err).is_empty());
823 }
824
825 #[test]
826 #[cfg(feature = "alloc")]
827 fn incompatible_height_error() {
828 let mined_at = BlockHeight::from_u32(700_000);
830 let chain_tip = BlockHeight::from_u32(800_000);
831
832 let lock_by_time = LockTime::from_512_second_intervals(70); let err = lock_by_time.is_satisfied_by_height(chain_tip, mined_at).unwrap_err();
834
835 let expected_time = NumberOf512Seconds::from_512_second_intervals(70);
836 assert_eq!(
837 err,
838 IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(expected_time))
839 );
840 assert!(!format!("{}", err).is_empty());
841 }
842
843 #[test]
844 #[cfg(feature = "alloc")]
845 fn invalid_height_error() {
846 let mined_at = BlockHeight::from_u32(900_000);
848 let chain_tip = BlockHeight::from_u32(800_000);
849
850 let block_count = NumberOfBlocks::from_height(70); let err = block_count.is_satisfied_by(chain_tip, mined_at).unwrap_err();
852
853 assert!(matches!(err, InvalidHeightError { chain_tip: _, utxo_mined_at: _ }));
854 }
855
856 #[test]
857 #[cfg(feature = "alloc")]
858 fn incompatible_time_error() {
859 let mined_at = BlockMtp::from_u32(1_234_567_890);
861 let chain_tip = BlockMtp::from_u32(1_600_000_000);
862
863 let lock_by_height = LockTime::from_height(10); let err = lock_by_height.is_satisfied_by_time(chain_tip, mined_at).unwrap_err();
865
866 let expected_height = NumberOfBlocks::from(10);
867 assert_eq!(
868 err,
869 IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(expected_height))
870 );
871 assert!(!format!("{}", err).is_empty());
872 }
873
874 #[test]
875 #[cfg(feature = "alloc")]
876 fn invalid_time_error() {
877 let mined_at = BlockMtp::from_u32(1_734_567_890);
879 let chain_tip = BlockMtp::from_u32(1_600_000_000);
880
881 let time_interval = NumberOf512Seconds::from_512_second_intervals(10); let err = time_interval.is_satisfied_by(chain_tip, mined_at).unwrap_err();
883
884 assert!(matches!(err, InvalidTimeError { chain_tip: _, utxo_mined_at: _ }));
885 }
886
887 #[test]
888 fn test_locktime_chain_state() {
889 fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
890 let mut timestamps = [BlockTime::from_u32(0); 11];
891 for (i, ts) in timestamps.iter_mut().enumerate() {
892 *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
893 }
894 timestamps
895 }
896
897 let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
898 let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
899
900 let chain_height = BlockHeight::from_u32(100);
901 let chain_mtp = BlockMtp::new(timestamps);
902 let utxo_height = BlockHeight::from_u32(80);
903 let utxo_mtp = BlockMtp::new(utxo_timestamps);
904
905 let lock1 = LockTime::Blocks(NumberOfBlocks::from(10));
906 assert!(lock1.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp).unwrap());
907
908 let lock2 = LockTime::Blocks(NumberOfBlocks::from(21));
909 assert!(lock2.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp).unwrap());
910
911 let lock3 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
912 assert!(lock3.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp).unwrap());
913
914 let lock4 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(20000));
915 assert!(!lock4.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp).unwrap());
916
917 assert!(LockTime::ZERO
918 .is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp)
919 .unwrap());
920 assert!(LockTime::from_512_second_intervals(0)
921 .is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp)
922 .unwrap());
923
924 let lock6 = LockTime::from_seconds_floor(5000).unwrap();
925 assert!(lock6.is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp).unwrap());
926
927 let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
928 assert!(!max_height_lock
929 .is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp)
930 .unwrap());
931
932 let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
933 assert!(!max_time_lock
934 .is_satisfied_by(chain_height, chain_mtp, utxo_height, utxo_mtp)
935 .unwrap());
936
937 let max_chain_height = BlockHeight::from_u32(u32::MAX);
938 let max_chain_mtp = BlockMtp::new(generate_timestamps(u32::MAX, 100));
939 let max_utxo_height = BlockHeight::MAX;
940 let max_utxo_mtp = max_chain_mtp;
941 assert!(!max_height_lock
942 .is_satisfied_by(max_chain_height, max_chain_mtp, max_utxo_height, max_utxo_mtp)
943 .unwrap());
944 assert!(!max_time_lock
945 .is_satisfied_by(max_chain_height, max_chain_mtp, max_utxo_height, max_utxo_mtp)
946 .unwrap());
947 }
948
949 #[test]
950 #[allow(deprecated_in_future)]
951 fn sanity_check() {
952 assert_eq!(LockTime::from(NumberOfBlocks::MAX).to_consensus_u32(), u32::from(u16::MAX));
953 assert_eq!(
954 NumberOf512Seconds::from_512_second_intervals(100).to_512_second_intervals(),
955 100u16
956 );
957 assert_eq!(
958 LockTime::from(NumberOf512Seconds::from_512_second_intervals(100)).to_consensus_u32(),
959 4_194_404u32
960 ); assert_eq!(NumberOf512Seconds::from_512_second_intervals(1).to_seconds(), 512);
962 }
963
964 #[test]
965 fn from_512_second_intervals_roundtrip() {
966 let intervals = 100_u16;
967 let locktime = NumberOf512Seconds::from_512_second_intervals(intervals);
968 assert_eq!(locktime.to_512_second_intervals(), intervals);
969 }
970
971 #[test]
972 fn from_seconds_ceil_success() {
973 let actual = NumberOf512Seconds::from_seconds_ceil(100).unwrap();
974 let expected = NumberOf512Seconds(1_u16);
975 assert_eq!(actual, expected);
976 }
977
978 #[test]
979 fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
980 let actual = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
981 let expected = NumberOf512Seconds(u16::MAX);
982 assert_eq!(actual, expected);
983 }
984
985 #[test]
986 fn from_seconds_ceil_causes_time_overflow_error() {
987 let result = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1);
988 assert!(result.is_err());
989 }
990
991 #[test]
992 fn from_seconds_floor_success() {
993 let actual = NumberOf512Seconds::from_seconds_floor(100).unwrap();
994 let expected = NumberOf512Seconds(0_u16);
995 assert_eq!(actual, expected);
996 }
997
998 #[test]
999 fn from_seconds_floor_with_exact_interval() {
1000 let actual = NumberOf512Seconds::from_seconds_floor(512).unwrap();
1001 let expected = NumberOf512Seconds(1_u16);
1002 assert_eq!(actual, expected);
1003 }
1004
1005 #[test]
1006 fn from_seconds_floor_with_maximum_encodable_seconds_success() {
1007 let actual =
1008 NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
1009 let expected = NumberOf512Seconds(u16::MAX);
1010 assert_eq!(actual, expected);
1011 }
1012
1013 #[test]
1014 fn from_seconds_floor_causes_time_overflow_error() {
1015 let result = NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512);
1016 assert!(result.is_err());
1017 }
1018
1019 fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
1020 let mut timestamps = [BlockTime::from_u32(0); 11];
1021 for (i, ts) in timestamps.iter_mut().enumerate() {
1022 *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
1023 }
1024 timestamps
1025 }
1026
1027 #[test]
1028 fn test_time_chain_state() {
1029 use crate::BlockMtp;
1030
1031 let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
1032 let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
1033
1034 let timestamps2: [BlockTime; 11] = generate_timestamps(1_599_995_119, 200);
1035 let utxo_timestamps2: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1036
1037 let timestamps3: [BlockTime; 11] = generate_timestamps(1_600_050_000, 200);
1038 let utxo_timestamps3: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1039
1040 let time_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
1043 let chain_state1 = BlockMtp::new(timestamps);
1044 let utxo_state1 = BlockMtp::new(utxo_timestamps);
1045 assert!(time_lock.is_satisfied_by_time(chain_state1, utxo_state1).unwrap());
1046
1047 let chain_state2 = BlockMtp::new(timestamps2);
1049 let utxo_state2 = BlockMtp::new(utxo_timestamps2);
1050 assert!(!time_lock.is_satisfied_by_time(chain_state2, utxo_state2).unwrap());
1051
1052 let larger_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(100));
1054 let chain_state3 = BlockMtp::new(timestamps3);
1055 let utxo_state3 = BlockMtp::new(utxo_timestamps3);
1056 assert!(larger_lock.is_satisfied_by_time(chain_state3, utxo_state3).unwrap());
1057
1058 let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
1060 let chain_state4 = BlockMtp::new(timestamps);
1061 let utxo_state4 = BlockMtp::new(utxo_timestamps);
1062 assert!(!max_time_lock.is_satisfied_by_time(chain_state4, utxo_state4).unwrap());
1063 }
1064
1065 #[test]
1066 fn test_height_chain_state() {
1067 let height_lock = LockTime::Blocks(NumberOfBlocks(10));
1068
1069 let chain_state1 = BlockHeight::from_u32(89);
1071 let utxo_state1 = BlockHeight::from_u32(80);
1072 assert!(height_lock.is_satisfied_by_height(chain_state1, utxo_state1).unwrap());
1073
1074 let chain_state2 = BlockHeight::from_u32(88);
1076 let utxo_state2 = BlockHeight::from_u32(80);
1077 assert!(!height_lock.is_satisfied_by_height(chain_state2, utxo_state2).unwrap());
1078
1079 let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
1081 let chain_state3 = BlockHeight::from_u32(1000);
1082 let utxo_state3 = BlockHeight::from_u32(80);
1083 assert!(!max_height_lock.is_satisfied_by_height(chain_state3, utxo_state3).unwrap());
1084 }
1085
1086 #[test]
1087 fn test_max_height_satisfaction() {
1088 let mined_at = BlockHeight::from_u32(u32::MIN);
1090 let chain_tip = BlockHeight::from_u32(u32::MAX);
1091
1092 let block_height = NumberOfBlocks::from(10); assert!(block_height.is_satisfied_by(chain_tip, mined_at).unwrap());
1094 }
1095}