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]
60#[spec_locked("5.4.7", "BIP65Check")]
61pub fn check_bip65(tx_locktime: u32, stack_locktime: u32) -> bool {
62 locktime_types_match(tx_locktime, stack_locktime) && tx_locktime >= stack_locktime
63}
64
65#[inline]
69pub fn locktime_types_match(locktime1: u32, locktime2: u32) -> bool {
70 get_locktime_type(locktime1) == get_locktime_type(locktime2)
71}
72
73pub fn decode_locktime_value(bytes: &[u8]) -> Option<u32> {
84 if bytes.len() > 5 {
85 return None; }
87
88 debug_assert!(
90 bytes.len() <= 5,
91 "Locktime byte string length ({}) must be <= 5",
92 bytes.len()
93 );
94
95 let mut value: u32 = 0;
96 for (i, &byte) in bytes.iter().enumerate() {
97 if i >= 4 {
98 break; }
100
101 debug_assert!(i < 4, "Byte index ({i}) must be < 4 for locktime decoding");
103
104 let shift_amount = i * 8;
106 debug_assert!(
107 shift_amount < 32,
108 "Shift amount ({shift_amount}) must be < 32 (i: {i})"
109 );
110
111 value |= (byte as u32) << shift_amount;
112 }
113
114 Some(value)
117}
118
119pub fn encode_locktime_value(value: u32) -> ByteString {
124 let mut bytes = Vec::new();
125
126 let mut temp = value;
128 while temp > 0 {
129 bytes.push((temp & 0xff) as u8);
130 temp >>= 8;
131
132 debug_assert!(
135 temp < value || bytes.len() <= 4,
136 "Locktime encoding loop must terminate (temp: {}, value: {}, bytes: {})",
137 temp,
138 value,
139 bytes.len()
140 );
141 }
142
143 if bytes.is_empty() {
145 bytes.push(0);
146 }
147
148 let len = bytes.len();
150 debug_assert!(
151 !bytes.is_empty() && len <= 4,
152 "Encoded locktime length ({len}) must be between 1 and 4 bytes"
153 );
154
155 bytes
156}
157
158#[inline]
167#[spec_locked("5.5", "ExtractSequenceTypeFlag")]
168#[blvm_spec_lock::ensures(result == false || sequence >= 4194304)]
169pub fn extract_sequence_type_flag(sequence: u32) -> bool {
170 (sequence & 0x00400000) != 0
171}
172
173#[inline]
177#[spec_locked("5.5", "ExtractSequenceLocktimeValue")]
178#[blvm_spec_lock::ensures(result <= 65535)]
179pub fn extract_sequence_locktime_value(sequence: u32) -> u16 {
180 (sequence & 0x0000ffff) as u16
181}
182
183#[inline]
190#[spec_locked("5.5", "IsSequenceDisabled")]
191#[blvm_spec_lock::ensures(result == false || sequence > 2147483647)]
192pub fn is_sequence_disabled(sequence: u32) -> bool {
193 (sequence & 0x80000000) != 0
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_get_locktime_type_block_height() {
202 assert_eq!(get_locktime_type(100), LocktimeType::BlockHeight);
203 assert_eq!(
204 get_locktime_type(LOCKTIME_THRESHOLD - 1),
205 LocktimeType::BlockHeight
206 );
207 }
208
209 #[test]
210 fn test_get_locktime_type_timestamp() {
211 assert_eq!(
212 get_locktime_type(LOCKTIME_THRESHOLD),
213 LocktimeType::Timestamp
214 );
215 assert_eq!(get_locktime_type(1_000_000_000), LocktimeType::Timestamp);
216 }
217
218 #[test]
219 fn test_locktime_types_match() {
220 assert!(locktime_types_match(100, 200));
221 assert!(locktime_types_match(
222 LOCKTIME_THRESHOLD,
223 LOCKTIME_THRESHOLD + 1000
224 ));
225 assert!(!locktime_types_match(100, LOCKTIME_THRESHOLD));
226 }
227
228 #[test]
229 fn test_decode_locktime_value() {
230 assert_eq!(decode_locktime_value(&[100, 0, 0, 0]), Some(100));
231 assert_eq!(decode_locktime_value(&[0]), Some(0));
232 assert_eq!(
233 decode_locktime_value(&[0xff, 0xff, 0xff, 0xff]),
234 Some(0xffffffff)
235 );
236 assert_eq!(decode_locktime_value(&[0; 6]), None); }
238
239 #[test]
240 fn test_encode_locktime_value() {
241 assert_eq!(encode_locktime_value(100), vec![100]); assert_eq!(encode_locktime_value(0), vec![0]);
244 assert_eq!(
245 encode_locktime_value(0x12345678),
246 vec![0x78, 0x56, 0x34, 0x12]
247 );
248 assert_eq!(encode_locktime_value(0x00001234), vec![0x34, 0x12]);
250 assert_eq!(
251 encode_locktime_value(0x12345600),
252 vec![0x00, 0x56, 0x34, 0x12]
253 );
254 }
255
256 #[test]
257 fn test_extract_sequence_type_flag() {
258 assert!(extract_sequence_type_flag(0x00400000));
259 assert!(!extract_sequence_type_flag(0x00000000));
260 assert!(extract_sequence_type_flag(0x00410000));
261 }
262
263 #[test]
264 fn test_extract_sequence_locktime_value() {
265 assert_eq!(extract_sequence_locktime_value(0x00001234), 0x1234);
266 assert_eq!(extract_sequence_locktime_value(0x00401234), 0x1234); }
268
269 #[test]
270 fn test_is_sequence_disabled() {
271 assert!(is_sequence_disabled(0x80000000));
272 assert!(!is_sequence_disabled(0x00000000));
273 assert!(is_sequence_disabled(0x80010000));
274 }
275}