1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// SPDX-License-Identifier: CC0-1.0

//! Absolute Locktimes

use core::{cmp, fmt};

use bitcoin::absolute;

/// Maximum allowed absolute locktime value.
pub const MAX_ABSOLUTE_LOCKTIME: u32 = 0x8000_0000;

/// Minimum allowed absolute locktime value.
///
/// In Bitcoin 0 is an allowed value, but in Miniscript it is not, because we
/// (ab)use the locktime value as a boolean in our Script fragments, and avoiding
/// this would reduce efficiency.
pub const MIN_ABSOLUTE_LOCKTIME: u32 = 1;

/// Error parsing an absolute locktime.
#[derive(Debug, PartialEq)]
pub struct AbsLockTimeError {
    value: u32,
}

impl fmt::Display for AbsLockTimeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.value < MIN_ABSOLUTE_LOCKTIME {
            f.write_str("absolute locktimes in Miniscript have a minimum value of 1")
        } else {
            debug_assert!(self.value > MAX_ABSOLUTE_LOCKTIME);
            write!(
                f,
                "absolute locktimes in Miniscript have a maximum value of 0x{:08x}; got 0x{:08x}",
                MAX_ABSOLUTE_LOCKTIME, self.value
            )
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for AbsLockTimeError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}

/// An absolute locktime that implements `Ord`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AbsLockTime(absolute::LockTime);

impl AbsLockTime {
    /// Constructs an `AbsLockTime` from an nLockTime value or the argument to `CHEKCLOCKTIMEVERIFY`.
    pub fn from_consensus(n: u32) -> Result<Self, AbsLockTimeError> {
        if n >= MIN_ABSOLUTE_LOCKTIME && n <= MAX_ABSOLUTE_LOCKTIME {
            Ok(AbsLockTime(absolute::LockTime::from_consensus(n)))
        } else {
            Err(AbsLockTimeError { value: n })
        }
    }

    /// Returns the inner `u32` value. This is the value used when creating this `LockTime`
    /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime.
    ///
    /// This calls through to `absolute::LockTime::to_consensus_u32()` and the same usage warnings
    /// apply.
    pub fn to_consensus_u32(self) -> u32 { self.0.to_consensus_u32() }

    /// Whether this is a height-based locktime.
    pub fn is_block_height(&self) -> bool { self.0.is_block_height() }

    /// Whether this is a time-based locktime.
    pub fn is_block_time(&self) -> bool { self.0.is_block_time() }
}

impl From<AbsLockTime> for absolute::LockTime {
    fn from(lock_time: AbsLockTime) -> absolute::LockTime { lock_time.0 }
}

impl cmp::PartialOrd for AbsLockTime {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
}

impl cmp::Ord for AbsLockTime {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        let this = self.0.to_consensus_u32();
        let that = other.0.to_consensus_u32();
        this.cmp(&that)
    }
}

impl fmt::Display for AbsLockTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}