bitcoin_script_analyzer/util/
locktime.rs

1use time::{format_description, OffsetDateTime};
2
3pub const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
4pub const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff;
5
6#[derive(PartialEq, Eq)]
7pub enum LocktimeType {
8    Height,
9    Time,
10}
11
12impl LocktimeType {
13    pub fn new(value: u32, relative: bool) -> Self {
14        if value
15            < if relative {
16                SEQUENCE_LOCKTIME_TYPE_FLAG
17            } else {
18                500000000
19            }
20        {
21            Self::Height
22        } else {
23            Self::Time
24        }
25    }
26}
27
28pub fn locktime_type_equals(a: u32, b: u32, relative: bool) -> bool {
29    LocktimeType::new(a, relative) == LocktimeType::new(b, relative)
30}
31
32// Output of these functions should fit the following sentence:
33// "This TXO becomes spendable ..."
34
35pub fn absolute_timelock_height_to_string(n: u32) -> String {
36    format!("at block {n}")
37}
38
39pub fn absolute_timelock_time_to_string(n: u32) -> String {
40    const DATE_FORMAT_STR: &str =
41        "on [year]-[month]-[day] [hour]:[minute]:[second] ([unix_timestamp] seconds since unix epoch)";
42
43    let date = OffsetDateTime::from_unix_timestamp(n as i64).unwrap();
44    let format = format_description::parse_borrowed::<2>(DATE_FORMAT_STR).unwrap();
45
46    date.format(&format).unwrap()
47}
48
49pub fn relative_timelock_height_to_string(n: u32) -> String {
50    format!("in {n} blocks")
51}
52
53pub fn relative_timelock_time_to_string(n: u32) -> String {
54    let mut t = (n & SEQUENCE_LOCKTIME_MASK) * 512;
55    let mut output = format!("in {}s", t % 60);
56    let mut prev = 60;
57    for unit in [('m', 60), ('h', 24), ('d', 999)] {
58        t /= prev;
59        if t == 0 {
60            break;
61        }
62        output.insert_str(3, &format!("{}{} ", t % unit.1, unit.0));
63        prev = unit.1;
64    }
65    output
66}
67
68pub fn locktime_to_string_unchecked(n: u32, relative: bool, type_: LocktimeType) -> String {
69    (match (relative, type_) {
70        (false, LocktimeType::Height) => absolute_timelock_height_to_string,
71        (false, LocktimeType::Time) => absolute_timelock_time_to_string,
72        (true, LocktimeType::Height) => relative_timelock_height_to_string,
73        (true, LocktimeType::Time) => relative_timelock_time_to_string,
74    })(n)
75}
76
77pub fn locktime_to_string(n: u32, relative: bool) -> String {
78    locktime_to_string_unchecked(n, relative, LocktimeType::new(n, relative))
79}