use crate::constants::LOCKTIME_THRESHOLD;
use crate::types::*;
use blvm_spec_lock::spec_locked;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LocktimeType {
BlockHeight,
Timestamp,
}
#[spec_locked("5.4.7")]
#[inline]
pub fn get_locktime_type(locktime: u32) -> LocktimeType {
if locktime < LOCKTIME_THRESHOLD {
LocktimeType::BlockHeight
} else {
LocktimeType::Timestamp
}
}
#[inline]
#[spec_locked("5.4.7")]
pub fn check_bip65(tx_locktime: u32, stack_locktime: u32) -> bool {
tx_locktime != 0
&& locktime_types_match(tx_locktime, stack_locktime)
&& tx_locktime >= stack_locktime
}
#[inline]
#[spec_locked("5.4.7")]
pub fn locktime_types_match(locktime1: u32, locktime2: u32) -> bool {
get_locktime_type(locktime1) == get_locktime_type(locktime2)
}
#[spec_locked("5.4.7")]
pub fn decode_locktime_value(bytes: &[u8]) -> Option<u32> {
if bytes.len() > 5 {
return None; }
debug_assert!(
bytes.len() <= 5,
"Locktime byte string length ({}) must be <= 5",
bytes.len()
);
let mut value: u32 = 0;
for (i, &byte) in bytes.iter().enumerate() {
if i >= 4 {
break; }
debug_assert!(i < 4, "Byte index ({i}) must be < 4 for locktime decoding");
let shift_amount = i * 8;
debug_assert!(
shift_amount < 32,
"Shift amount ({shift_amount}) must be < 32 (i: {i})"
);
value |= (byte as u32) << shift_amount;
}
Some(value)
}
#[spec_locked("5.4.7")]
pub fn encode_locktime_value(value: u32) -> ByteString {
let mut bytes = Vec::new();
let mut temp = value;
while temp > 0 {
bytes.push((temp & 0xff) as u8);
temp >>= 8;
debug_assert!(
temp < value || bytes.len() <= 4,
"Locktime encoding loop must terminate (temp: {}, value: {}, bytes: {})",
temp,
value,
bytes.len()
);
}
if bytes.is_empty() {
bytes.push(0);
}
let len = bytes.len();
debug_assert!(
!bytes.is_empty() && len <= 4,
"Encoded locktime length ({len}) must be between 1 and 4 bytes"
);
bytes
}
#[inline]
#[spec_locked("5.5")]
pub fn extract_sequence_type_flag(sequence: u32) -> bool {
(sequence & 0x00400000) != 0
}
#[inline]
#[spec_locked("5.5")]
pub fn extract_sequence_locktime_value(sequence: u32) -> u16 {
(sequence & 0x0000ffff) as u16
}
#[inline]
#[spec_locked("5.5")]
pub fn is_sequence_disabled(sequence: u32) -> bool {
(sequence & 0x80000000) != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_locktime_type_block_height() {
assert_eq!(get_locktime_type(100), LocktimeType::BlockHeight);
assert_eq!(
get_locktime_type(LOCKTIME_THRESHOLD - 1),
LocktimeType::BlockHeight
);
}
#[test]
fn test_get_locktime_type_timestamp() {
assert_eq!(
get_locktime_type(LOCKTIME_THRESHOLD),
LocktimeType::Timestamp
);
assert_eq!(get_locktime_type(1_000_000_000), LocktimeType::Timestamp);
}
#[test]
fn test_locktime_types_match() {
assert!(locktime_types_match(100, 200));
assert!(locktime_types_match(
LOCKTIME_THRESHOLD,
LOCKTIME_THRESHOLD + 1000
));
assert!(!locktime_types_match(100, LOCKTIME_THRESHOLD));
}
#[test]
fn test_decode_locktime_value() {
assert_eq!(decode_locktime_value(&vec![100, 0, 0, 0]), Some(100));
assert_eq!(decode_locktime_value(&vec![0]), Some(0));
assert_eq!(
decode_locktime_value(&vec![0xff, 0xff, 0xff, 0xff]),
Some(0xffffffff)
);
assert_eq!(decode_locktime_value(&vec![0; 6]), None); }
#[test]
fn test_encode_locktime_value() {
assert_eq!(encode_locktime_value(100), vec![100]); assert_eq!(encode_locktime_value(0), vec![0]);
assert_eq!(
encode_locktime_value(0x12345678),
vec![0x78, 0x56, 0x34, 0x12]
);
assert_eq!(encode_locktime_value(0x00001234), vec![0x34, 0x12]);
assert_eq!(
encode_locktime_value(0x12345600),
vec![0x00, 0x56, 0x34, 0x12]
);
}
#[test]
fn test_extract_sequence_type_flag() {
assert!(extract_sequence_type_flag(0x00400000));
assert!(!extract_sequence_type_flag(0x00000000));
assert!(extract_sequence_type_flag(0x00410000));
}
#[test]
fn test_extract_sequence_locktime_value() {
assert_eq!(extract_sequence_locktime_value(0x00001234), 0x1234);
assert_eq!(extract_sequence_locktime_value(0x00401234), 0x1234); }
#[test]
fn test_is_sequence_disabled() {
assert!(is_sequence_disabled(0x80000000));
assert!(!is_sequence_disabled(0x00000000));
assert!(is_sequence_disabled(0x80010000));
}
}