use core::convert::TryFrom;
use core::fmt;
#[cfg(all(test, mutate))]
use mutagen::mutate;
use crate::parse::impl_parse_str_from_int_infallible;
use crate::prelude::*;
#[cfg(doc)]
use crate::relative;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub enum LockTime {
Blocks(Height),
Time(Time),
}
impl LockTime {
#[inline]
#[cfg_attr(all(test, mutate), mutate)]
pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool {
if let Ok(true) = self.is_satisfied_by_height(h) {
true
} else {
matches!(self.is_satisfied_by_time(t), Ok(true))
}
}
#[inline]
#[cfg_attr(all(test, mutate), mutate)]
pub fn is_implied_by(&self, other: LockTime) -> bool {
use LockTime::*;
match (*self, other) {
(Blocks(this), Blocks(other)) => this.value() <= other.value(),
(Time(this), Time(other)) => this.value() <= other.value(),
_ => false, }
}
#[inline]
#[cfg_attr(all(test, mutate), mutate)]
pub fn is_satisfied_by_height(&self, h: Height) -> Result<bool, Error> {
use LockTime::*;
match *self {
Blocks(ref height) => Ok(height.value() <= h.value()),
Time(ref time) => Err(Error::IncompatibleTime(*self, *time)),
}
}
#[inline]
#[cfg_attr(all(test, mutate), mutate)]
pub fn is_satisfied_by_time(&self, t: Time) -> Result<bool, Error> {
use LockTime::*;
match *self {
Time(ref time) => Ok(time.value() <= t.value()),
Blocks(ref height) => Err(Error::IncompatibleHeight(*self, *height)),
}
}
}
impl From<Height> for LockTime {
#[inline]
fn from(h: Height) -> Self { LockTime::Blocks(h) }
}
impl From<Time> for LockTime {
#[inline]
fn from(t: Time) -> Self { LockTime::Time(t) }
}
impl fmt::Display for LockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*;
if f.alternate() {
match *self {
Blocks(ref h) => write!(f, "block-height {}", h),
Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
}
} else {
match *self {
Blocks(ref h) => fmt::Display::fmt(h, f),
Time(ref t) => fmt::Display::fmt(t, f),
}
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Height(u16);
impl Height {
pub const ZERO: Self = Height(0);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = Height(u16::max_value());
#[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
pub const fn min_value() -> Self { Self::MIN }
#[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
pub const fn max_value() -> Self { Self::MAX }
#[inline]
pub fn value(self) -> u16 { self.0 }
}
impl From<u16> for Height {
#[inline]
fn from(value: u16) -> Self { Height(value) }
}
impl_parse_str_from_int_infallible!(Height, u16, from);
impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Time(u16);
impl Time {
pub const ZERO: Self = Time(0);
pub const MIN: Self = Time::ZERO;
pub const MAX: Self = Time(u16::max_value());
#[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
pub const fn min_value() -> Self { Self::MIN }
#[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
pub const fn max_value() -> Self { Self::MAX }
#[inline]
pub fn from_512_second_intervals(intervals: u16) -> Self { Time(intervals) }
#[inline]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, Error> {
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
Ok(Time::from_512_second_intervals(interval))
} else {
Err(Error::IntegerOverflow(seconds))
}
}
#[inline]
pub fn value(self) -> u16 { self.0 }
}
impl_parse_str_from_int_infallible!(Time, u16, from_512_second_intervals);
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
IntegerOverflow(u32),
IncompatibleHeight(LockTime, Height),
IncompatibleTime(LockTime, Time),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match *self {
IntegerOverflow(val) => write!(
f,
"{} seconds is too large to be encoded to a 16 bit 512 second interval",
val
),
IncompatibleHeight(lock, height) =>
write!(f, "tried to satisfy lock {} with height: {}", lock, height),
IncompatibleTime(lock, time) =>
write!(f, "tried to satisfy lock {} with time: {}", lock, time),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match *self {
IntegerOverflow(_) | IncompatibleHeight(_, _) | IncompatibleTime(_, _) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn satisfied_by_height() {
let height = Height::from(10);
let time = Time::from_512_second_intervals(70);
let lock = LockTime::from(height);
assert!(!lock.is_satisfied_by(Height::from(9), time));
assert!(lock.is_satisfied_by(Height::from(10), time));
assert!(lock.is_satisfied_by(Height::from(11), time));
}
#[test]
fn satisfied_by_time() {
let height = Height::from(10);
let time = Time::from_512_second_intervals(70);
let lock = LockTime::from(time);
assert!(!lock.is_satisfied_by(height, Time::from_512_second_intervals(69)));
assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(70)));
assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(71)));
}
#[test]
fn height_correctly_implies() {
let height = Height::from(10);
let lock = LockTime::from(height);
assert!(!lock.is_implied_by(LockTime::from(Height::from(9))));
assert!(lock.is_implied_by(LockTime::from(Height::from(10))));
assert!(lock.is_implied_by(LockTime::from(Height::from(11))));
}
#[test]
fn time_correctly_implies() {
let time = Time::from_512_second_intervals(70);
let lock = LockTime::from(time);
assert!(!lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(69))));
assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(70))));
assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(71))));
}
#[test]
fn incorrect_units_do_not_imply() {
let time = Time::from_512_second_intervals(70);
let height = Height::from(10);
let lock = LockTime::from(time);
assert!(!lock.is_implied_by(LockTime::from(height)));
}
}