1pub mod error;
13
14use core::{convert, fmt};
15
16#[cfg(feature = "arbitrary")]
17use arbitrary::{Arbitrary, Unstructured};
18use internals::const_casts;
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21
22use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
23#[cfg(doc)]
24use crate::relative;
25use crate::{BlockHeight, BlockMtp, Sequence};
26
27#[rustfmt::skip] #[doc(no_inline)]
29pub use self::error::{
30 DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, InvalidHeightError,
31 InvalidTimeError, IsSatisfiedByError, IsSatisfiedByHeightError, IsSatisfiedByTimeError,
32 TimeOverflowError,
33};
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum LockTime {
52 Blocks(NumberOfBlocks),
54 Time(NumberOf512Seconds),
56}
57
58impl LockTime {
59 pub const ZERO: Self = Self::Blocks(NumberOfBlocks::ZERO);
62
63 pub const SIZE: usize = 4; #[inline]
97 pub fn from_consensus(n: u32) -> Result<Self, DisabledLockTimeError> {
98 let sequence = crate::Sequence::from_consensus(n);
99 sequence.to_relative_lock_time().ok_or(DisabledLockTimeError(n))
100 }
101
102 #[inline]
110 pub fn to_consensus_u32(self) -> u32 {
111 match self {
112 Self::Blocks(ref h) => u32::from(h.to_count()),
113 Self::Time(ref t) => Sequence::LOCK_TYPE_MASK | u32::from(t.to_512_second_intervals()),
114 }
115 }
116
117 #[inline]
139 pub fn from_sequence(n: Sequence) -> Result<Self, DisabledLockTimeError> {
140 Self::from_consensus(n.to_consensus_u32())
141 }
142
143 #[inline]
145 pub fn to_sequence(self) -> Sequence { Sequence::from_consensus(self.to_consensus_u32()) }
146
147 #[inline]
149 pub const fn from_block_count(n: u16) -> Self { Self::Blocks(NumberOfBlocks::from_count(n)) }
150
151 #[inline]
157 pub const fn from_512_second_intervals(intervals: u16) -> Self {
158 Self::Time(NumberOf512Seconds::from_512_second_intervals(intervals))
159 }
160
161 #[inline]
168 pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
169 match NumberOf512Seconds::from_seconds_floor(seconds) {
170 Ok(time) => Ok(Self::Time(time)),
171 Err(e) => Err(e),
172 }
173 }
174
175 #[inline]
182 pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
183 match NumberOf512Seconds::from_seconds_ceil(seconds) {
184 Ok(time) => Ok(Self::Time(time)),
185 Err(e) => Err(e),
186 }
187 }
188
189 #[inline]
192 pub const fn is_same_unit(self, other: Self) -> bool {
193 matches!((self, other), (Self::Blocks(_), Self::Blocks(_)) | (Self::Time(_), Self::Time(_)))
194 }
195
196 #[inline]
198 pub const fn is_block_count(self) -> bool { matches!(self, Self::Blocks(_)) }
199
200 #[inline]
202 pub const fn is_block_time(self) -> bool { !self.is_block_count() }
203
204 #[inline]
220 pub fn is_satisfied_by(
221 self,
222 chain_tip_height: BlockHeight,
223 chain_tip_mtp: BlockMtp,
224 utxo_mined_at_height: Option<BlockHeight>,
225 utxo_mined_at_mtp: Option<BlockMtp>,
226 ) -> Result<bool, IsSatisfiedByError> {
227 match self {
228 Self::Blocks(blocks) => blocks
229 .is_satisfied_by(chain_tip_height, utxo_mined_at_height)
230 .map_err(IsSatisfiedByError::Blocks),
231 Self::Time(time) => time
232 .is_satisfied_by(chain_tip_mtp, utxo_mined_at_mtp)
233 .map_err(IsSatisfiedByError::Time),
234 }
235 }
236
237 #[inline]
248 pub fn is_satisfied_by_height(
249 self,
250 chain_tip: BlockHeight,
251 utxo_mined_at: Option<BlockHeight>,
252 ) -> Result<bool, IsSatisfiedByHeightError> {
253 match self {
254 Self::Blocks(blocks) => blocks
255 .is_satisfied_by(chain_tip, utxo_mined_at)
256 .map_err(IsSatisfiedByHeightError::Satisfaction),
257 Self::Time(time) =>
258 Err(IncompatibleHeightError(time)).map_err(IsSatisfiedByHeightError::Incompatible),
259 }
260 }
261
262 #[inline]
273 pub fn is_satisfied_by_time(
274 self,
275 chain_tip: BlockMtp,
276 utxo_mined_at: Option<BlockMtp>,
277 ) -> Result<bool, IsSatisfiedByTimeError> {
278 match self {
279 Self::Time(time) => time
280 .is_satisfied_by(chain_tip, utxo_mined_at)
281 .map_err(IsSatisfiedByTimeError::Satisfaction),
282 Self::Blocks(blocks) =>
283 Err(IncompatibleTimeError(blocks)).map_err(IsSatisfiedByTimeError::Incompatible),
284 }
285 }
286
287 #[inline]
318 pub fn is_implied_by(self, other: Self) -> bool {
319 match (self, other) {
320 (Self::Blocks(this), Self::Blocks(other)) => this <= other,
321 (Self::Time(this), Self::Time(other)) => this <= other,
322 _ => false, }
324 }
325
326 #[inline]
348 pub fn is_implied_by_sequence(self, other: Sequence) -> bool {
349 Self::from_sequence(other).is_ok_and(|other| self.is_implied_by(other))
350 }
351}
352
353impl From<NumberOfBlocks> for LockTime {
354 #[inline]
355 fn from(h: NumberOfBlocks) -> Self { Self::Blocks(h) }
356}
357
358impl From<NumberOf512Seconds> for LockTime {
359 #[inline]
360 fn from(t: NumberOf512Seconds) -> Self { Self::Time(t) }
361}
362
363impl fmt::Display for LockTime {
364 #[inline]
365 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366 if f.alternate() {
367 match *self {
368 Self::Blocks(ref h) => write!(f, "block-count {}", h),
369 Self::Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
370 }
371 } else {
372 match *self {
373 Self::Blocks(ref h) => fmt::Display::fmt(h, f),
374 Self::Time(ref t) => fmt::Display::fmt(t, f),
375 }
376 }
377 }
378}
379
380impl convert::TryFrom<Sequence> for LockTime {
381 type Error = DisabledLockTimeError;
382 #[inline]
383 fn try_from(seq: Sequence) -> Result<Self, DisabledLockTimeError> { Self::from_sequence(seq) }
384}
385
386impl From<LockTime> for Sequence {
387 #[inline]
388 fn from(lt: LockTime) -> Self { lt.to_sequence() }
389}
390
391#[cfg(feature = "serde")]
392impl serde::Serialize for LockTime {
393 #[inline]
394 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
395 where
396 S: serde::Serializer,
397 {
398 self.to_consensus_u32().serialize(serializer)
399 }
400}
401
402#[cfg(feature = "serde")]
403impl<'de> serde::Deserialize<'de> for LockTime {
404 #[inline]
405 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
406 where
407 D: serde::Deserializer<'de>,
408 {
409 u32::deserialize(deserializer)
410 .and_then(|n| Self::from_consensus(n).map_err(serde::de::Error::custom))
411 }
412}
413
414#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
416pub struct NumberOfBlocks(u16);
417
418impl NumberOfBlocks {
419 pub const ZERO: Self = Self(0);
421
422 pub const MIN: Self = Self::ZERO;
424
425 pub const MAX: Self = Self(u16::MAX);
427
428 #[inline]
430 pub const fn from_count(blocks: u16) -> Self { Self(blocks) }
431
432 #[inline]
434 #[must_use]
435 pub const fn to_count(self) -> u16 { self.0 }
436
437 #[inline]
444 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
445 let block_count = parse_int::hex_u16_prefixed(s)?;
446 Ok(Self::from_count(block_count))
447 }
448
449 #[inline]
456 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
457 let block_count = parse_int::hex_u16_unprefixed(s)?;
458 Ok(Self::from_count(block_count))
459 }
460
461 #[inline]
475 pub fn is_satisfied_by(
476 self,
477 chain_tip: crate::BlockHeight,
478 utxo_mined_at: Option<crate::BlockHeight>,
479 ) -> Result<bool, InvalidHeightError> {
480 utxo_mined_at.map_or(
481 Ok(self == Self::ZERO),
484 |mined_at| {
485 chain_tip
486 .checked_sub(mined_at)
487 .ok_or(InvalidHeightError { chain_tip, utxo_mined_at: mined_at })
488 .map(|diff| u32::from(self.to_count()).saturating_sub(1) <= diff.to_u32())
489 },
490 )
491 }
492}
493
494crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOfBlocks);
495
496parse_int::impl_parse_str_from_int_infallible!(NumberOfBlocks, u16, from_count);
497
498impl fmt::Display for NumberOfBlocks {
499 #[inline]
500 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
501}
502#[cfg(feature = "serde")]
503impl Serialize for NumberOfBlocks {
504 #[inline]
505 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
506 where
507 S: Serializer,
508 {
509 u16::serialize(&self.to_count(), s)
510 }
511}
512
513#[cfg(feature = "serde")]
514impl<'de> Deserialize<'de> for NumberOfBlocks {
515 #[inline]
516 fn deserialize<D>(d: D) -> Result<Self, D::Error>
517 where
518 D: Deserializer<'de>,
519 {
520 Ok(Self::from_count(u16::deserialize(d)?))
521 }
522}
523
524#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
528pub struct NumberOf512Seconds(u16);
529
530impl NumberOf512Seconds {
531 pub const ZERO: Self = Self(0);
533
534 pub const MIN: Self = Self::ZERO;
536
537 pub const MAX: Self = Self(u16::MAX);
539
540 #[inline]
545 pub const fn from_512_second_intervals(intervals: u16) -> Self { Self(intervals) }
546
547 #[inline]
549 #[must_use]
550 pub const fn to_512_second_intervals(self) -> u16 { self.0 }
551
552 #[inline]
559 #[rustfmt::skip] pub const fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
561 let interval = seconds / 512;
562 if interval <= u16::MAX as u32 { Ok(Self::from_512_second_intervals(interval as u16)) } else {
565 Err(TimeOverflowError { seconds })
566 }
567 }
568
569 #[inline]
576 #[rustfmt::skip] pub const fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
578 if seconds <= u16::MAX as u32 * 512 {
579 let interval = seconds.div_ceil(512);
580 Ok(Self::from_512_second_intervals(interval as u16)) } else {
582 Err(TimeOverflowError { seconds })
583 }
584 }
585
586 #[inline]
588 pub const fn to_seconds(self) -> u32 { const_casts::u16_to_u32(self.0) * 512 }
589
590 #[inline]
597 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
598 let block_count = parse_int::hex_u16_prefixed(s)?;
599 Ok(Self::from_512_second_intervals(block_count))
600 }
601
602 #[inline]
609 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
610 let block_count = parse_int::hex_u16_unprefixed(s)?;
611 Ok(Self::from_512_second_intervals(block_count))
612 }
613
614 #[inline]
625 pub fn is_satisfied_by(
626 self,
627 chain_tip: crate::BlockMtp,
628 utxo_mined_at: Option<crate::BlockMtp>,
629 ) -> Result<bool, InvalidTimeError> {
630 utxo_mined_at.map_or(
631 Ok(self == Self::ZERO),
634 |mined_at| {
635 chain_tip
636 .checked_sub(mined_at)
637 .ok_or(InvalidTimeError { chain_tip, utxo_mined_at: mined_at })
638 .map(|diff| self.to_seconds() <= diff.to_u32())
639 },
640 )
641 }
642}
643
644crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(NumberOf512Seconds);
645
646parse_int::impl_parse_str_from_int_infallible!(NumberOf512Seconds, u16, from_512_second_intervals);
647
648impl fmt::Display for NumberOf512Seconds {
649 #[inline]
650 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
651}
652
653#[cfg(feature = "serde")]
654impl Serialize for NumberOf512Seconds {
655 #[inline]
656 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
657 where
658 S: Serializer,
659 {
660 u16::serialize(&self.to_512_second_intervals(), s)
661 }
662}
663
664#[cfg(feature = "serde")]
665impl<'de> Deserialize<'de> for NumberOf512Seconds {
666 #[inline]
667 fn deserialize<D>(d: D) -> Result<Self, D::Error>
668 where
669 D: Deserializer<'de>,
670 {
671 Ok(Self::from_512_second_intervals(u16::deserialize(d)?))
672 }
673}
674
675#[cfg(feature = "arbitrary")]
676impl<'a> Arbitrary<'a> for LockTime {
677 #[inline]
678 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
679 let choice = u.int_in_range(0..=1)?;
680
681 match choice {
682 0 => Ok(Self::Blocks(NumberOfBlocks::arbitrary(u)?)),
683 _ => Ok(Self::Time(NumberOf512Seconds::arbitrary(u)?)),
684 }
685 }
686}
687
688#[cfg(feature = "arbitrary")]
689impl<'a> Arbitrary<'a> for NumberOfBlocks {
690 #[inline]
691 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
692 let choice = u.int_in_range(0..=2)?;
693
694 match choice {
695 0 => Ok(Self::MIN),
696 1 => Ok(Self::MAX),
697 _ => Ok(Self::from_count(u16::arbitrary(u)?)),
698 }
699 }
700}
701
702#[cfg(feature = "arbitrary")]
703impl<'a> Arbitrary<'a> for NumberOf512Seconds {
704 #[inline]
705 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
706 let choice = u.int_in_range(0..=2)?;
707
708 match choice {
709 0 => Ok(Self::MIN),
710 1 => Ok(Self::MAX),
711 _ => Ok(Self::from_512_second_intervals(u16::arbitrary(u)?)),
712 }
713 }
714}
715
716#[cfg(test)]
717mod tests {
718 #[cfg(feature = "alloc")]
719 use alloc::format;
720
721 use super::*;
722 use crate::{BlockHeight, BlockTime};
723
724 const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
725
726 #[test]
727 #[cfg(feature = "alloc")]
728 fn display_and_alternate() {
729 let lock_by_height = LockTime::from_block_count(10);
730 let lock_by_time = LockTime::from_512_second_intervals(70);
731
732 assert_eq!(format!("{}", lock_by_height), "10");
733 assert_eq!(format!("{:#}", lock_by_height), "block-count 10");
734 assert!(!format!("{:?}", lock_by_height).is_empty());
735
736 assert_eq!(format!("{}", lock_by_time), "70");
737 assert_eq!(format!("{:#}", lock_by_time), "block-time 70 (512 second intervals)");
738 assert!(!format!("{:?}", lock_by_time).is_empty());
739 }
740
741 #[test]
742 fn from_seconds_ceil_and_floor() {
743 let time = 70 * 512 + 1;
744 let lock_by_time = LockTime::from_seconds_ceil(time).unwrap();
745 assert_eq!(lock_by_time, LockTime::from_512_second_intervals(71));
746
747 let lock_by_time = LockTime::from_seconds_floor(time).unwrap();
748 assert_eq!(lock_by_time, LockTime::from_512_second_intervals(70));
749
750 let mut max_time = 0xffff * 512;
751 assert_eq!(LockTime::from_seconds_ceil(max_time), LockTime::from_seconds_floor(max_time));
752 max_time += 512;
753 assert!(LockTime::from_seconds_ceil(max_time).is_err());
754 assert!(LockTime::from_seconds_floor(max_time).is_err());
755 }
756
757 #[test]
758 fn parses_correctly_to_height_or_time() {
759 let height1 = NumberOfBlocks::from_count(10);
760 let height2 = NumberOfBlocks::from_count(11);
761 let time1 = NumberOf512Seconds::from_512_second_intervals(70);
762 let time2 = NumberOf512Seconds::from_512_second_intervals(71);
763
764 let lock_by_height1 = LockTime::from(height1);
765 let lock_by_height2 = LockTime::from(height2);
766 let lock_by_time1 = LockTime::from(time1);
767 let lock_by_time2 = LockTime::from(time2);
768
769 assert!(lock_by_height1.is_block_count());
770 assert!(!lock_by_height1.is_block_time());
771
772 assert!(!lock_by_time1.is_block_count());
773 assert!(lock_by_time1.is_block_time());
774
775 assert!(lock_by_height1.is_same_unit(lock_by_height2));
777 assert!(!lock_by_height1.is_same_unit(lock_by_time1));
778 assert!(lock_by_time1.is_same_unit(lock_by_time2));
779 assert!(!lock_by_time1.is_same_unit(lock_by_height1));
780 }
781
782 #[test]
783 fn height_correctly_implies() {
784 let height = NumberOfBlocks::from_count(10);
785 let lock_by_height = LockTime::from(height);
786
787 assert!(!lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(9))));
788 assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(10))));
789 assert!(lock_by_height.is_implied_by(LockTime::from(NumberOfBlocks::from_count(11))));
790 }
791
792 #[test]
793 fn time_correctly_implies() {
794 let time = NumberOf512Seconds::from_512_second_intervals(70);
795 let lock_by_time = LockTime::from(time);
796
797 assert!(!lock_by_time
798 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(69))));
799 assert!(lock_by_time
800 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(70))));
801 assert!(lock_by_time
802 .is_implied_by(LockTime::from(NumberOf512Seconds::from_512_second_intervals(71))));
803 }
804
805 #[test]
806 fn sequence_correctly_implies() {
807 let height = NumberOfBlocks::from_count(10);
808 let time = NumberOf512Seconds::from_512_second_intervals(70);
809
810 let lock_by_height = LockTime::from(height);
811 let lock_by_time = LockTime::from(time);
812
813 let seq_height = Sequence::from(lock_by_height);
814 let seq_time = Sequence::from(lock_by_time);
815
816 assert!(lock_by_height.is_implied_by_sequence(seq_height));
817 assert!(!lock_by_height.is_implied_by_sequence(seq_time));
818
819 assert!(lock_by_time.is_implied_by_sequence(seq_time));
820 assert!(!lock_by_time.is_implied_by_sequence(seq_height));
821
822 let disabled_sequence = Sequence::from_consensus(1 << 31);
823 assert!(!lock_by_height.is_implied_by_sequence(disabled_sequence));
824 assert!(!lock_by_time.is_implied_by_sequence(disabled_sequence));
825 }
826
827 #[test]
828 fn incorrect_units_do_not_imply() {
829 let time = NumberOf512Seconds::from_512_second_intervals(70);
830 let height = NumberOfBlocks::from_count(10);
831
832 let lock_by_time = LockTime::from(time);
833 assert!(!lock_by_time.is_implied_by(LockTime::from(height)));
834 }
835
836 #[test]
837 fn consensus_round_trip() {
838 assert!(LockTime::from_consensus(1 << 31).is_err());
839 assert!(LockTime::from_consensus(1 << 30).is_ok());
840 assert_eq!(LockTime::from_consensus(65536), LockTime::from_consensus(0));
842
843 for val in [0u32, 1, 1000, 65535] {
844 let seq = Sequence::from_consensus(val);
845 let lt = LockTime::from_consensus(val).unwrap();
846 assert_eq!(lt.to_consensus_u32(), val);
847 assert_eq!(lt.to_sequence(), seq);
848 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
849
850 let seq = Sequence::from_consensus(val + (1 << 22));
851 let lt = LockTime::from_consensus(val + (1 << 22)).unwrap();
852 assert_eq!(lt.to_consensus_u32(), val + (1 << 22));
853 assert_eq!(lt.to_sequence(), seq);
854 assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
855 }
856 }
857
858 #[test]
859 #[cfg(feature = "alloc")]
860 fn disabled_locktime_error() {
861 let disabled_sequence = Sequence::from_consensus(1 << 31);
862 let err = LockTime::try_from(disabled_sequence).unwrap_err();
863
864 assert_eq!(err.disabled_locktime_value(), 1 << 31);
865 assert!(!format!("{}", err).is_empty());
866 }
867
868 #[test]
869 #[cfg(feature = "alloc")]
870 fn incompatible_height_error() {
871 let mined_at = BlockHeight::from_u32(700_000);
873 let chain_tip = BlockHeight::from_u32(800_000);
874
875 let lock_by_time = LockTime::from_512_second_intervals(70); let err = lock_by_time.is_satisfied_by_height(chain_tip, Some(mined_at)).unwrap_err();
877
878 let expected_time = NumberOf512Seconds::from_512_second_intervals(70);
879 assert_eq!(
880 err,
881 IsSatisfiedByHeightError::Incompatible(IncompatibleHeightError(expected_time))
882 );
883 assert!(!format!("{}", err).is_empty());
884 }
885
886 #[test]
887 #[cfg(feature = "alloc")]
888 fn invalid_height_error() {
889 let mined_at = BlockHeight::from_u32(900_000);
891 let chain_tip = BlockHeight::from_u32(800_000);
892
893 let block_count = NumberOfBlocks::from_count(70); let err = block_count.is_satisfied_by(chain_tip, Some(mined_at)).unwrap_err();
895
896 assert!(matches!(err, InvalidHeightError { chain_tip: _, utxo_mined_at: _ }));
897 }
898
899 #[test]
900 #[cfg(feature = "alloc")]
901 fn incompatible_time_error() {
902 let mined_at = BlockMtp::from_u32(1_234_567_890);
904 let chain_tip = BlockMtp::from_u32(1_600_000_000);
905
906 let lock_by_height = LockTime::from_block_count(10); let err = lock_by_height.is_satisfied_by_time(chain_tip, Some(mined_at)).unwrap_err();
908
909 let expected_height = NumberOfBlocks::from_count(10);
910 assert_eq!(
911 err,
912 IsSatisfiedByTimeError::Incompatible(IncompatibleTimeError(expected_height))
913 );
914 assert!(!format!("{}", err).is_empty());
915 }
916
917 #[test]
918 #[cfg(feature = "alloc")]
919 fn invalid_time_error() {
920 let mined_at = BlockMtp::from_u32(1_734_567_890);
922 let chain_tip = BlockMtp::from_u32(1_600_000_000);
923
924 let time_interval = NumberOf512Seconds::from_512_second_intervals(10); let err = time_interval.is_satisfied_by(chain_tip, Some(mined_at)).unwrap_err();
926
927 assert!(matches!(err, InvalidTimeError { chain_tip: _, utxo_mined_at: _ }));
928 }
929
930 #[test]
931 fn locktime_chain_state() {
932 fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
933 let mut timestamps = [BlockTime::from_u32(0); 11];
934 for (i, ts) in timestamps.iter_mut().enumerate() {
935 *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
936 }
937 timestamps
938 }
939
940 let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
941 let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
942
943 let chain_height = BlockHeight::from_u32(100);
944 let chain_mtp = BlockMtp::new(timestamps);
945 let utxo_height = BlockHeight::from_u32(80);
946 let utxo_mtp = BlockMtp::new(utxo_timestamps);
947
948 let lock1 = LockTime::Blocks(NumberOfBlocks::from_count(10));
949 assert!(lock1
950 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
951 .unwrap());
952
953 let lock2 = LockTime::Blocks(NumberOfBlocks::from_count(21));
954 assert!(lock2
955 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
956 .unwrap());
957
958 let lock3 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
959 assert!(lock3
960 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
961 .unwrap());
962
963 let lock4 = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(20000));
964 assert!(!lock4
965 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
966 .unwrap());
967
968 assert!(LockTime::ZERO
969 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
970 .unwrap());
971 assert!(LockTime::from_512_second_intervals(0)
972 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
973 .unwrap());
974
975 let lock6 = LockTime::from_seconds_floor(5000).unwrap();
976 assert!(lock6
977 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
978 .unwrap());
979
980 let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
981 assert!(!max_height_lock
982 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
983 .unwrap());
984
985 let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
986 assert!(!max_time_lock
987 .is_satisfied_by(chain_height, chain_mtp, Some(utxo_height), Some(utxo_mtp))
988 .unwrap());
989
990 let max_chain_height = BlockHeight::from_u32(u32::MAX);
991 let max_chain_mtp = BlockMtp::new(generate_timestamps(u32::MAX, 100));
992 let max_utxo_height = BlockHeight::MAX;
993 let max_utxo_mtp = max_chain_mtp;
994 assert!(!max_height_lock
995 .is_satisfied_by(
996 max_chain_height,
997 max_chain_mtp,
998 Some(max_utxo_height),
999 Some(max_utxo_mtp)
1000 )
1001 .unwrap());
1002 assert!(!max_time_lock
1003 .is_satisfied_by(
1004 max_chain_height,
1005 max_chain_mtp,
1006 Some(max_utxo_height),
1007 Some(max_utxo_mtp)
1008 )
1009 .unwrap());
1010 }
1011
1012 #[test]
1013 fn sanity_check() {
1014 assert_eq!(LockTime::from(NumberOfBlocks::MAX).to_consensus_u32(), u32::from(u16::MAX));
1015 assert_eq!(
1016 NumberOf512Seconds::from_512_second_intervals(100).to_512_second_intervals(),
1017 100u16
1018 );
1019 assert_eq!(
1020 LockTime::from(NumberOf512Seconds::from_512_second_intervals(100)).to_consensus_u32(),
1021 4_194_404u32
1022 ); assert_eq!(NumberOf512Seconds::from_512_second_intervals(1).to_seconds(), 512);
1024 }
1025
1026 #[test]
1027 fn from_512_second_intervals_roundtrip() {
1028 let intervals = 100_u16;
1029 let locktime = NumberOf512Seconds::from_512_second_intervals(intervals);
1030 assert_eq!(locktime.to_512_second_intervals(), intervals);
1031 }
1032
1033 #[test]
1034 fn from_seconds_ceil_success() {
1035 let actual = NumberOf512Seconds::from_seconds_ceil(100).unwrap();
1036 let expected = NumberOf512Seconds(1_u16);
1037 assert_eq!(actual, expected);
1038 }
1039
1040 #[test]
1041 fn from_seconds_ceil_with_maximum_encodable_seconds_success() {
1042 let actual = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS).unwrap();
1043 let expected = NumberOf512Seconds(u16::MAX);
1044 assert_eq!(actual, expected);
1045 }
1046
1047 #[test]
1048 fn from_seconds_ceil_causes_time_overflow_error() {
1049 let result = NumberOf512Seconds::from_seconds_ceil(MAXIMUM_ENCODABLE_SECONDS + 1);
1050 assert!(result.is_err());
1051 }
1052
1053 #[test]
1054 fn from_seconds_floor_success() {
1055 let actual = NumberOf512Seconds::from_seconds_floor(100).unwrap();
1056 let expected = NumberOf512Seconds(0_u16);
1057 assert_eq!(actual, expected);
1058 }
1059
1060 #[test]
1061 fn from_seconds_floor_with_exact_interval() {
1062 let actual = NumberOf512Seconds::from_seconds_floor(512).unwrap();
1063 let expected = NumberOf512Seconds(1_u16);
1064 assert_eq!(actual, expected);
1065 }
1066
1067 #[test]
1068 fn from_seconds_floor_with_maximum_encodable_seconds_success() {
1069 let actual =
1070 NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 511).unwrap();
1071 let expected = NumberOf512Seconds(u16::MAX);
1072 assert_eq!(actual, expected);
1073 }
1074
1075 #[test]
1076 fn from_seconds_floor_causes_time_overflow_error() {
1077 let result = NumberOf512Seconds::from_seconds_floor(MAXIMUM_ENCODABLE_SECONDS + 512);
1078 assert!(result.is_err());
1079 }
1080
1081 fn generate_timestamps(start: u32, step: u16) -> [BlockTime; 11] {
1082 let mut timestamps = [BlockTime::from_u32(0); 11];
1083 for (i, ts) in timestamps.iter_mut().enumerate() {
1084 *ts = BlockTime::from_u32(start.saturating_sub((step * i as u16).into()));
1085 }
1086 timestamps
1087 }
1088
1089 #[test]
1090 fn time_chain_state() {
1091 use crate::BlockMtp;
1092
1093 let timestamps: [BlockTime; 11] = generate_timestamps(1_600_000_000, 200);
1094 let utxo_timestamps: [BlockTime; 11] = generate_timestamps(1_599_000_000, 200);
1095
1096 let timestamps2: [BlockTime; 11] = generate_timestamps(1_599_995_119, 200);
1097 let utxo_timestamps2: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1098
1099 let timestamps3: [BlockTime; 11] = generate_timestamps(1_600_050_000, 200);
1100 let utxo_timestamps3: [BlockTime; 11] = generate_timestamps(1_599_990_000, 200);
1101
1102 let time_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(10));
1105 let chain_state1 = BlockMtp::new(timestamps);
1106 let utxo_state1 = BlockMtp::new(utxo_timestamps);
1107 assert!(time_lock.is_satisfied_by_time(chain_state1, Some(utxo_state1)).unwrap());
1108
1109 let chain_state2 = BlockMtp::new(timestamps2);
1111 let utxo_state2 = BlockMtp::new(utxo_timestamps2);
1112 assert!(!time_lock.is_satisfied_by_time(chain_state2, Some(utxo_state2)).unwrap());
1113
1114 let larger_lock = LockTime::Time(NumberOf512Seconds::from_512_second_intervals(100));
1116 let chain_state3 = BlockMtp::new(timestamps3);
1117 let utxo_state3 = BlockMtp::new(utxo_timestamps3);
1118 assert!(larger_lock.is_satisfied_by_time(chain_state3, Some(utxo_state3)).unwrap());
1119
1120 let max_time_lock = LockTime::Time(NumberOf512Seconds::MAX);
1122 let chain_state4 = BlockMtp::new(timestamps);
1123 let utxo_state4 = BlockMtp::new(utxo_timestamps);
1124 assert!(!max_time_lock.is_satisfied_by_time(chain_state4, Some(utxo_state4)).unwrap());
1125 }
1126
1127 #[test]
1128 fn height_chain_state() {
1129 let height_lock = LockTime::Blocks(NumberOfBlocks(10));
1130
1131 let chain_state1 = BlockHeight::from_u32(89);
1133 let utxo_state1 = BlockHeight::from_u32(80);
1134 assert!(height_lock.is_satisfied_by_height(chain_state1, Some(utxo_state1)).unwrap());
1135
1136 let chain_state2 = BlockHeight::from_u32(88);
1138 let utxo_state2 = BlockHeight::from_u32(80);
1139 assert!(!height_lock.is_satisfied_by_height(chain_state2, Some(utxo_state2)).unwrap());
1140
1141 let max_height_lock = LockTime::Blocks(NumberOfBlocks::MAX);
1143 let chain_state3 = BlockHeight::from_u32(1000);
1144 let utxo_state3 = BlockHeight::from_u32(80);
1145 assert!(!max_height_lock.is_satisfied_by_height(chain_state3, Some(utxo_state3)).unwrap());
1146 }
1147 #[test]
1148 fn parent_child_can_be_mined_in_the_same_block_lock_by_count_if_zero() {
1149 let height_lock = LockTime::Blocks(NumberOfBlocks::ZERO);
1150 let chain_state = BlockHeight::from_u32(100);
1152
1153 assert!(height_lock.is_satisfied_by_height(chain_state, None).unwrap());
1154 }
1155
1156 #[test]
1157 fn parent_child_cannot_be_mined_in_the_same_block_lock_by_count_if_non_zero() {
1158 let height_lock = LockTime::from_block_count(1);
1159 let chain_state = BlockHeight::from_u32(100);
1161
1162 assert!(!height_lock.is_satisfied_by_height(chain_state, None).unwrap());
1163 }
1164
1165 #[test]
1166 fn parent_child_can_be_mined_in_the_same_block_lock_by_time_if_zero() {
1167 let height_lock = LockTime::Time(NumberOf512Seconds::ZERO);
1168 let chain_state = crate::BlockMtp::from_u32(83_176_862);
1170
1171 assert!(height_lock.is_satisfied_by_time(chain_state, None).unwrap());
1172 }
1173
1174 #[test]
1175 fn parent_child_cannot_be_mined_in_the_same_block_lock_by_time_if_non_zero() {
1176 let height_lock = LockTime::from_512_second_intervals(1);
1177 let chain_state = crate::BlockMtp::from_u32(83_176_862);
1179
1180 assert!(!height_lock.is_satisfied_by_time(chain_state, None).unwrap());
1181 }
1182
1183 #[test]
1184 fn satisfied_by_height_boundary() {
1185 for n in [0, 2, 5, 9, 20, u16::MAX] {
1186 let lock = NumberOfBlocks::from_count(n);
1187 let mined_at = BlockHeight::from_u32(100);
1188
1189 let first_spendable_tip = BlockHeight::from_u32(100 + u32::from(n).saturating_sub(1));
1190 assert!(
1191 lock.is_satisfied_by(first_spendable_tip, Some(mined_at)).unwrap(),
1192 "n={n} must be satisfied at tip {first_spendable_tip} ({n} confirmations)"
1193 );
1194
1195 if n > 1 {
1196 let too_early = BlockHeight::from_u32(100 + u32::from(n) - 2);
1197 assert!(
1198 !lock.is_satisfied_by(too_early, Some(mined_at)).unwrap(),
1199 "n={n} must not be satisfied at tip {too_early}"
1200 );
1201 }
1202 }
1203 }
1204
1205 #[test]
1206 fn max_height_satisfaction() {
1207 let mined_at = BlockHeight::from_u32(u32::MIN);
1209 let chain_tip = BlockHeight::from_u32(u32::MAX);
1210
1211 let block_height = NumberOfBlocks::from_count(10); assert!(block_height.is_satisfied_by(chain_tip, Some(mined_at)).unwrap());
1213 }
1214}