Skip to content

Commit fc6bfa8

Browse files
committed
introduce RelLockTime type
Just like AbsLockTime, this gives us a locktime which implements Ord and which cannot be constructed unless it meets the invariants specified by Miniscript.
1 parent 637720a commit fc6bfa8

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ pub use crate::miniscript::satisfy::{Preimage32, Satisfier};
151151
pub use crate::miniscript::{hash256, Miniscript};
152152
use crate::prelude::*;
153153
pub use crate::primitives::absolute_locktime::{AbsLockTime, AbsLockTimeError};
154+
pub use crate::primitives::relative_locktime::{RelLockTime, RelLockTimeError};
154155

155156
/// Public key trait which can be converted to Hash type
156157
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
@@ -505,6 +506,8 @@ pub enum Error {
505506
MultipathDescLenMismatch,
506507
/// Invalid absolute locktime
507508
AbsoluteLockTime(AbsLockTimeError),
509+
/// Invalid absolute locktime
510+
RelativeLockTime(RelLockTimeError),
508511
}
509512

510513
// https://github.com/sipa/miniscript/pull/5 for discussion on this number
@@ -581,6 +584,7 @@ impl fmt::Display for Error {
581584
Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
582585
Error::MultipathDescLenMismatch => write!(f, "At least two BIP389 key expressions in the descriptor contain tuples of derivation indexes of different lengths"),
583586
Error::AbsoluteLockTime(ref e) => e.fmt(f),
587+
Error::RelativeLockTime(ref e) => e.fmt(f),
584588
}
585589
}
586590
}
@@ -634,6 +638,7 @@ impl error::Error for Error {
634638
AnalysisError(e) => Some(e),
635639
PubKeyCtxError(e, _) => Some(e),
636640
AbsoluteLockTime(e) => Some(e),
641+
RelativeLockTime(e) => Some(e),
637642
}
638643
}
639644
}

src/primitives/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
//! should be re-exported at the crate root.
1313
1414
pub mod absolute_locktime;
15+
pub mod relative_locktime;

src/primitives/relative_locktime.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Relative Locktimes
4+
5+
use core::{cmp, convert, fmt};
6+
7+
use bitcoin::{relative, Sequence};
8+
9+
/// Error parsing an absolute locktime.
10+
#[derive(Debug, PartialEq)]
11+
pub struct RelLockTimeError {
12+
value: u32,
13+
}
14+
15+
impl fmt::Display for RelLockTimeError {
16+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17+
if self.value == 0 {
18+
f.write_str("relative locktimes in Miniscript have a minimum value of 1")
19+
} else {
20+
debug_assert!(Sequence::from_consensus(self.value)
21+
.to_relative_lock_time()
22+
.is_none());
23+
write!(f, "locktime value {} is not a valid BIP68 relative locktime", self.value)
24+
}
25+
}
26+
}
27+
28+
#[cfg(feature = "std")]
29+
impl std::error::Error for RelLockTimeError {
30+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
31+
}
32+
33+
/// A relative locktime which implements `Ord`.
34+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35+
pub struct RelLockTime(Sequence);
36+
37+
impl RelLockTime {
38+
/// The "0 blocks" constant.
39+
pub const ZERO: Self = RelLockTime(Sequence::ZERO);
40+
41+
/// Constructs an `RelLockTime` from an nLockTime value or the argument to `CHEKCLOCKTIMEVERIFY`.
42+
pub fn from_consensus(n: u32) -> Result<Self, RelLockTimeError> {
43+
convert::TryFrom::try_from(Sequence::from_consensus(n))
44+
}
45+
46+
/// Returns the inner `u32` value. This is the value used when creating this `LockTime`
47+
/// i.e., `n OP_CHECKSEQUENCEVERIFY` or `nSequence`.
48+
pub fn to_consensus_u32(self) -> u32 { self.0.to_consensus_u32() }
49+
50+
/// Takes a 16-bit number of blocks and produces a relative locktime from it.
51+
pub fn from_height(height: u16) -> Self { RelLockTime(Sequence::from_height(height)) }
52+
53+
/// Takes a 16-bit number of 512-second time intervals and produces a relative locktime from it.
54+
pub fn from_512_second_intervals(time: u16) -> Self {
55+
RelLockTime(Sequence::from_512_second_intervals(time))
56+
}
57+
58+
/// Whether this timelock is blockheight-based.
59+
pub fn is_height_locked(&self) -> bool { self.0.is_height_locked() }
60+
61+
/// Whether this timelock is time-based.
62+
pub fn is_time_locked(&self) -> bool { self.0.is_time_locked() }
63+
}
64+
65+
impl convert::TryFrom<Sequence> for RelLockTime {
66+
type Error = RelLockTimeError;
67+
fn try_from(seq: Sequence) -> Result<Self, RelLockTimeError> {
68+
if seq.is_relative_lock_time() {
69+
Ok(RelLockTime(seq))
70+
} else {
71+
Err(RelLockTimeError { value: seq.to_consensus_u32() })
72+
}
73+
}
74+
}
75+
76+
impl From<RelLockTime> for Sequence {
77+
fn from(lock_time: RelLockTime) -> Sequence { lock_time.0 }
78+
}
79+
80+
impl From<RelLockTime> for relative::LockTime {
81+
fn from(lock_time: RelLockTime) -> relative::LockTime {
82+
lock_time.0.to_relative_lock_time().unwrap()
83+
}
84+
}
85+
86+
impl cmp::PartialOrd for RelLockTime {
87+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
88+
}
89+
90+
impl cmp::Ord for RelLockTime {
91+
fn cmp(&self, other: &Self) -> cmp::Ordering {
92+
let this = self.0.to_consensus_u32();
93+
let that = other.0.to_consensus_u32();
94+
this.cmp(&that)
95+
}
96+
}
97+
98+
impl fmt::Display for RelLockTime {
99+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
100+
}

0 commit comments

Comments
 (0)