blvm_consensus/
locktime.rs1use crate::constants::LOCKTIME_THRESHOLD;
7use crate::types::*;
8use blvm_spec_lock::spec_locked;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum LocktimeType {
13 BlockHeight,
15 Timestamp,
17}
18
19#[inline]
27#[spec_locked("5.4.7", "GetLocktimeType")]
28#[blvm_spec_lock::requires(locktime < LOCKTIME_THRESHOLD)]
29#[blvm_spec_lock::ensures(result == LocktimeType::BlockHeight)]
30pub fn get_locktime_type(locktime: u32) -> LocktimeType {
31 if locktime < LOCKTIME_THRESHOLD {
32 LocktimeType::BlockHeight
33 } else {
34 LocktimeType::Timestamp
35 }
36}
37
38#[inline]
43#[spec_locked("5.4.7", "GetLocktimeType")]
44#[blvm_spec_lock::requires(locktime >= LOCKTIME_THRESHOLD)]
45#[blvm_spec_lock::ensures(result == LocktimeType::Timestamp)]
46pub fn get_locktime_type_timestamp(locktime: u32) -> LocktimeType {
47 if locktime < LOCKTIME_THRESHOLD {
48 LocktimeType::BlockHeight
49 } else {
50 LocktimeType::Timestamp
51 }
52}
53
54#[inline]
57#[spec_locked("5.4.7", "BIP65Check")]
60#[blvm_spec_lock::ensures(result == false || tx_locktime != 0)]
61pub fn check_bip65(tx_locktime: u32, stack_locktime: u32) -> bool {
62 tx_locktime != 0
63 && locktime_types_match(tx_locktime, stack_locktime)
64 && tx_locktime >= stack_locktime
65}
66
67#[inline]
71pub fn locktime_types_match(locktime1: u32, locktime2: u32) -> bool {
72 get_locktime_type(locktime1) == get_locktime_type(locktime2)
73}
74
75pub fn decode_locktime_value(bytes: &[u8]) -> Option<u32> {
86 if bytes.len() > 5 {
87 return None; }
89
90 debug_assert!(
92 bytes.len() <= 5,
93 "Locktime byte string length ({}) must be <= 5",
94 bytes.len()
95 );
96
97 let mut value: u32 = 0;
98 for (i, &byte) in bytes.iter().enumerate() {
99 if i >= 4 {
100 break; }
102
103 debug_assert!(i < 4, "Byte index ({i}) must be < 4 for locktime decoding");
105
106 let shift_amount = i * 8;
108 debug_assert!(
109 shift_amount < 32,
110 "Shift amount ({shift_amount}) must be < 32 (i: {i})"
111 );
112
113 value |= (byte as u32) << shift_amount;
114 }
115
116 Some(value)
119}
120
121pub fn encode_locktime_value(value: u32) -> ByteString {
126 let mut bytes = Vec::new();
127
128 let mut temp = value;
130 while temp > 0 {
131 bytes.push((temp & 0xff) as u8);
132 temp >>= 8;
133
134 debug_assert!(
137 temp < value || bytes.len() <= 4,
138 "Locktime encoding loop must terminate (temp: {}, value: {}, bytes: {})",
139 temp,
140 value,
141 bytes.len()
142 );
143 }
144
145 if bytes.is_empty() {
147 bytes.push(0);
148 }
149
150 let len = bytes.len();
152 debug_assert!(
153 !bytes.is_empty() && len <= 4,
154 "Encoded locktime length ({len}) must be between 1 and 4 bytes"
155 );
156
157 bytes
158}
159
160#[inline]
169#[spec_locked("5.5", "ExtractSequenceTypeFlag")]
170#[blvm_spec_lock::ensures(result == false || sequence >= 4194304)]
171pub fn extract_sequence_type_flag(sequence: u32) -> bool {
172 (sequence & 0x00400000) != 0
173}
174
175#[inline]
179#[spec_locked("5.5", "ExtractSequenceLocktimeValue")]
180#[blvm_spec_lock::ensures(result <= 65535)]
181pub fn extract_sequence_locktime_value(sequence: u32) -> u16 {
182 (sequence & 0x0000ffff) as u16
183}
184
185#[inline]
192#[spec_locked("5.5", "IsSequenceDisabled")]
193#[blvm_spec_lock::ensures(result == false || sequence > 2147483647)]
194pub fn is_sequence_disabled(sequence: u32) -> bool {
195 (sequence & 0x80000000) != 0
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_get_locktime_type_block_height() {
204 assert_eq!(get_locktime_type(100), LocktimeType::BlockHeight);
205 assert_eq!(
206 get_locktime_type(LOCKTIME_THRESHOLD - 1),
207 LocktimeType::BlockHeight
208 );
209 }
210
211 #[test]
212 fn test_get_locktime_type_timestamp() {
213 assert_eq!(
214 get_locktime_type(LOCKTIME_THRESHOLD),
215 LocktimeType::Timestamp
216 );
217 assert_eq!(get_locktime_type(1_000_000_000), LocktimeType::Timestamp);
218 }
219
220 #[test]
221 fn test_locktime_types_match() {
222 assert!(locktime_types_match(100, 200));
223 assert!(locktime_types_match(
224 LOCKTIME_THRESHOLD,
225 LOCKTIME_THRESHOLD + 1000
226 ));
227 assert!(!locktime_types_match(100, LOCKTIME_THRESHOLD));
228 }
229
230 #[test]
231 fn test_decode_locktime_value() {
232 assert_eq!(decode_locktime_value(&[100, 0, 0, 0]), Some(100));
233 assert_eq!(decode_locktime_value(&[0]), Some(0));
234 assert_eq!(
235 decode_locktime_value(&[0xff, 0xff, 0xff, 0xff]),
236 Some(0xffffffff)
237 );
238 assert_eq!(decode_locktime_value(&[0; 6]), None); }
240
241 #[test]
242 fn test_encode_locktime_value() {
243 assert_eq!(encode_locktime_value(100), vec![100]); assert_eq!(encode_locktime_value(0), vec![0]);
246 assert_eq!(
247 encode_locktime_value(0x12345678),
248 vec![0x78, 0x56, 0x34, 0x12]
249 );
250 assert_eq!(encode_locktime_value(0x00001234), vec![0x34, 0x12]);
252 assert_eq!(
253 encode_locktime_value(0x12345600),
254 vec![0x00, 0x56, 0x34, 0x12]
255 );
256 }
257
258 #[test]
259 fn test_extract_sequence_type_flag() {
260 assert!(extract_sequence_type_flag(0x00400000));
261 assert!(!extract_sequence_type_flag(0x00000000));
262 assert!(extract_sequence_type_flag(0x00410000));
263 }
264
265 #[test]
266 fn test_extract_sequence_locktime_value() {
267 assert_eq!(extract_sequence_locktime_value(0x00001234), 0x1234);
268 assert_eq!(extract_sequence_locktime_value(0x00401234), 0x1234); }
270
271 #[test]
272 fn test_is_sequence_disabled() {
273 assert!(is_sequence_disabled(0x80000000));
274 assert!(!is_sequence_disabled(0x00000000));
275 assert!(is_sequence_disabled(0x80010000));
276 }
277}