Skip to content

Commit 59f8593

Browse files
committed
Use new bitcoin::timelock module
Use the new timelock module (open PR) from `rust-bitcoin`. This patch is an example of consuming the new timelock API.
1 parent 85cd0d8 commit 59f8593

File tree

17 files changed

+202
-198
lines changed

17 files changed

+202
-198
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use-serde = ["serde", "bitcoin/use-serde"]
1919
rand = ["bitcoin/rand"]
2020

2121
[dependencies]
22-
bitcoin = "0.28.0"
22+
bitcoin = { git = "https://github.com/tcharding/rust-bitcoin", branch = "05-17-timelock" }
2323
serde = { version = "1.0", optional = true}
2424

2525
[[example]]

src/interpreter/error.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::{error, fmt};
1616

1717
use bitcoin::hashes::hash160;
1818
use bitcoin::hashes::hex::ToHex;
19+
use bitcoin::timelock;
1920
use bitcoin::util::taproot;
2021
use bitcoin::{self, secp256k1};
2122

@@ -25,7 +26,7 @@ use super::BitcoinKey;
2526
#[derive(Debug)]
2627
pub enum Error {
2728
/// Could not satisfy, absolute locktime not met
28-
AbsoluteLocktimeNotMet(u32),
29+
AbsoluteLocktimeNotMet(timelock::Abs),
2930
/// Cannot Infer a taproot descriptor
3031
/// Key spends cannot infer the internal key of the descriptor
3132
/// Inferring script spends is possible, but is hidden nodes are currently
@@ -90,7 +91,7 @@ pub enum Error {
9091
/// Parse Error while parsing a `stack::Element::Push` as a XOnlyPublicKey (32 bytes)
9192
XOnlyPublicKeyParseError,
9293
/// Could not satisfy, relative locktime not met
93-
RelativeLocktimeNotMet(u32),
94+
RelativeLocktimeNotMet(timelock::Rel),
9495
/// Forward-secp related errors
9596
Secp(secp256k1::Error),
9697
/// Miniscript requires the entire top level script to be satisfied.
@@ -101,6 +102,8 @@ pub enum Error {
101102
SighashError(bitcoin::util::sighash::Error),
102103
/// Taproot Annex Unsupported
103104
TapAnnexUnsupported,
105+
/// Invalid comparison of timelocks.
106+
TimelockComparisonInvalid(timelock::Error),
104107
/// An uncompressed public key was encountered in a context where it is
105108
/// disallowed (e.g. in a Segwit script or p2wpkh output)
106109
UncompressedPubkey,
@@ -196,8 +199,9 @@ impl fmt::Display for Error {
196199
Error::AbsoluteLocktimeNotMet(n) => write!(
197200
f,
198201
"required absolute locktime CLTV of {} blocks, not met",
199-
n
202+
n,
200203
),
204+
Error::TimelockComparisonInvalid(e) => write!(f, "timelock comparison error: {:?}", e),
201205
Error::CannotInferTrDescriptors => write!(f, "Cannot infer taproot descriptors"),
202206
Error::ControlBlockParse(ref e) => write!(f, "Control block parse error {}", e),
203207
Error::ControlBlockVerificationError => {

src/interpreter/mod.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use std::str::FromStr;
2525

2626
use bitcoin::blockdata::witness::Witness;
2727
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
28+
use bitcoin::timelock;
2829
use bitcoin::util::{sighash, taproot};
2930
use bitcoin::{self, secp256k1, TxOut};
3031

@@ -486,13 +487,13 @@ pub enum SatisfiedConstraint {
486487
},
487488
///Relative Timelock for CSV.
488489
RelativeTimelock {
489-
/// The value of RelativeTimelock
490-
time: u32,
490+
/// The relative timelock.
491+
time: timelock::Rel,
491492
},
492493
///Absolute Timelock for CLTV.
493494
AbsoluteTimelock {
494-
/// The value of Absolute timelock
495-
time: u32,
495+
/// The absolute timelock.
496+
time: timelock::Abs,
496497
},
497498
}
498499

@@ -1197,7 +1198,7 @@ mod tests {
11971198
let after_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
11981199
assert_eq!(
11991200
after_satisfied.unwrap(),
1200-
vec![SatisfiedConstraint::AbsoluteTimelock { time: 1000 }]
1201+
vec![SatisfiedConstraint::AbsoluteTimelock { time: 1000.into() }]
12011202
);
12021203

12031204
//Check Older
@@ -1207,7 +1208,7 @@ mod tests {
12071208
let older_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
12081209
assert_eq!(
12091210
older_satisfied.unwrap(),
1210-
vec![SatisfiedConstraint::RelativeTimelock { time: 1000 }]
1211+
vec![SatisfiedConstraint::RelativeTimelock { time: 1000.into() }],
12111212
);
12121213

12131214
//Check Sha256

src/interpreter/stack.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use bitcoin;
1818
use bitcoin::blockdata::{opcodes, script};
1919
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
20+
use bitcoin::timelock;
2021

2122
use super::error::PkEvalErrInner;
2223
use super::{
@@ -230,14 +231,16 @@ impl<'txin> Stack<'txin> {
230231
/// booleans
231232
pub(super) fn evaluate_after(
232233
&mut self,
233-
n: &u32,
234+
n: &timelock::Abs,
234235
age: u32,
235236
) -> Option<Result<SatisfiedConstraint, Error>> {
236-
if age >= *n {
237-
self.push(Element::Satisfied);
238-
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { time: *n }))
239-
} else {
240-
Some(Err(Error::AbsoluteLocktimeNotMet(*n)))
237+
match n.is_expired(age) {
238+
Ok(true) => {
239+
self.push(Element::Satisfied);
240+
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { time: *n }))
241+
}
242+
Ok(false) => Some(Err(Error::AbsoluteLocktimeNotMet(*n))),
243+
Err(e) => Some(Err(Error::TimelockComparisonInvalid(e))),
241244
}
242245
}
243246

@@ -249,14 +252,16 @@ impl<'txin> Stack<'txin> {
249252
/// booleans
250253
pub(super) fn evaluate_older(
251254
&mut self,
252-
n: &u32,
255+
n: &timelock::Rel,
253256
height: u32,
254257
) -> Option<Result<SatisfiedConstraint, Error>> {
255-
if height >= *n {
256-
self.push(Element::Satisfied);
257-
Some(Ok(SatisfiedConstraint::RelativeTimelock { time: *n }))
258-
} else {
259-
Some(Err(Error::RelativeLocktimeNotMet(*n)))
258+
match n.is_expired(height) {
259+
Ok(true) => {
260+
self.push(Element::Satisfied);
261+
Some(Ok(SatisfiedConstraint::RelativeTimelock { time: *n }))
262+
}
263+
Ok(false) => Some(Err(Error::RelativeLocktimeNotMet(*n))),
264+
Err(e) => Some(Err(Error::TimelockComparisonInvalid(e))),
260265
}
261266
}
262267

src/miniscript/astelem.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,10 @@ where
491491
expression::terminal(&top.args[0], |x| Pk::Hash::from_str(x).map(Terminal::PkH))
492492
}
493493
("after", 1) => expression::terminal(&top.args[0], |x| {
494-
expression::parse_num(x).map(Terminal::After)
494+
expression::parse_num(x).map(|x| Terminal::After(x.into()))
495495
}),
496496
("older", 1) => expression::terminal(&top.args[0], |x| {
497-
expression::parse_num(x).map(Terminal::Older)
497+
expression::parse_num(x).map(|x| Terminal::Older(x.into()))
498498
}),
499499
("sha256", 1) => expression::terminal(&top.args[0], |x| {
500500
sha256::Hash::from_hex(x).map(Terminal::Sha256)
@@ -653,9 +653,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
653653
.push_slice(&Pk::hash_to_hash160(hash)[..])
654654
.push_opcode(opcodes::all::OP_EQUALVERIFY),
655655
Terminal::After(t) => builder
656-
.push_int(t as i64)
656+
.push_int(t.to_u32() as i64)
657657
.push_opcode(opcodes::all::OP_CLTV),
658-
Terminal::Older(t) => builder.push_int(t as i64).push_opcode(opcodes::all::OP_CSV),
658+
Terminal::Older(t) => builder
659+
.push_int(t.to_u32() as i64)
660+
.push_opcode(opcodes::all::OP_CSV),
659661
Terminal::Sha256(h) => builder
660662
.push_opcode(opcodes::all::OP_SIZE)
661663
.push_int(32)
@@ -788,8 +790,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
788790
match *self {
789791
Terminal::PkK(ref pk) => Ctx::pk_len(pk),
790792
Terminal::PkH(..) => 24,
791-
Terminal::After(n) => script_num_size(n as usize) + 1,
792-
Terminal::Older(n) => script_num_size(n as usize) + 1,
793+
Terminal::After(n) => script_num_size(n.to_u32() as usize) + 1,
794+
Terminal::Older(n) => script_num_size(n.to_u32() as usize) + 1,
793795
Terminal::Sha256(..) => 33 + 6,
794796
Terminal::Hash256(..) => 33 + 6,
795797
Terminal::Ripemd160(..) => 21 + 6,

src/miniscript/decode.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::{error, fmt};
2323

2424
use bitcoin::blockdata::constants::MAX_BLOCK_WEIGHT;
2525
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
26+
use bitcoin::timelock;
2627

2728
use crate::miniscript::lex::{Token as Tk, TokenIter};
2829
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
@@ -133,9 +134,9 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
133134
PkH(Pk::Hash),
134135
// timelocks
135136
/// `n CHECKLOCKTIMEVERIFY`
136-
After(u32),
137+
After(timelock::Abs),
137138
/// `n CHECKSEQUENCEVERIFY`
138-
Older(u32),
139+
Older(timelock::Rel),
139140
// hashlocks
140141
/// `SIZE 32 EQUALVERIFY SHA256 <hash> EQUAL`
141142
Sha256(sha256::Hash),
@@ -386,9 +387,9 @@ pub fn parse<Ctx: ScriptContext>(
386387
},
387388
// timelocks
388389
Tk::CheckSequenceVerify, Tk::Num(n)
389-
=> term.reduce0(Terminal::Older(n))?,
390+
=> term.reduce0(Terminal::Older(n.into()))?,
390391
Tk::CheckLockTimeVerify, Tk::Num(n)
391-
=> term.reduce0(Terminal::After(n))?,
392+
=> term.reduce0(Terminal::After(n.into()))?,
392393
// hashlocks
393394
Tk::Equal => match_token!(
394395
tokens,

src/miniscript/limits.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,6 @@ pub const MAX_SCRIPT_SIZE: usize = 10_000;
1414
/// Maximum script size allowed by standardness rules
1515
// https://github.com/bitcoin/bitcoin/blob/283a73d7eaea2907a6f7f800f529a0d6db53d7a6/src/policy/policy.h#L44
1616
pub const MAX_STANDARD_P2WSH_SCRIPT_SIZE: usize = 3600;
17-
/// The Threshold for deciding whether `nLockTime` is interpreted as
18-
/// time or height.
19-
// https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
20-
pub const LOCKTIME_THRESHOLD: u32 = 500_000_000;
21-
22-
/// Bit flag for deciding whether sequence number is
23-
/// interpreted as height or time
24-
/* If nSequence encodes a relative lock-time and this flag
25-
* is set, the relative lock-time has units of 512 seconds,
26-
* otherwise it specifies blocks with a granularity of 1. */
27-
// https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
28-
pub const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
29-
30-
/// Disable flag for sequence locktime
31-
/* Below flags apply in the context of BIP 68*/
32-
/* If this flag set, nSequence is NOT interpreted as a
33-
* relative lock-time. For future soft-fork compatibility*/
34-
// https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
35-
pub const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31;
36-
3717
/// Maximum script element size allowed by consensus rules
3818
// https://github.com/bitcoin/bitcoin/blob/42b66a6b814bca130a9ccf0a3f747cf33d628232/src/script/script.h#L23
3919
pub const MAX_SCRIPT_ELEMENT_SIZE: usize = 520;

src/miniscript/satisfy.rs

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ use std::{cmp, i64, mem};
2525
use bitcoin;
2626
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2727
use bitcoin::secp256k1::XOnlyPublicKey;
28+
use bitcoin::timelock;
2829
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash};
2930

30-
use crate::miniscript::limits::{
31-
LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
32-
};
3331
use crate::util::witness_size;
3432
use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
3533

@@ -110,56 +108,36 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
110108
}
111109

112110
/// Assert whether an relative locktime is satisfied
113-
fn check_older(&self, _: u32) -> bool {
111+
fn check_older(&self, _: timelock::Rel) -> bool {
114112
false
115113
}
116114

117115
/// Assert whether a absolute locktime is satisfied
118-
fn check_after(&self, _: u32) -> bool {
116+
fn check_after(&self, _: timelock::Abs) -> bool {
119117
false
120118
}
121119
}
122120

123121
// Allow use of `()` as a "no conditions available" satisfier
124122
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for () {}
125123

126-
/// Newtype around `u32` which implements `Satisfier` using `n` as an
127-
/// relative locktime
128-
pub struct Older(pub u32);
129-
130-
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
131-
fn check_older(&self, n: u32) -> bool {
132-
if self.0 & SEQUENCE_LOCKTIME_DISABLE_FLAG != 0 {
124+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for timelock::Rel {
125+
fn check_older(&self, n: timelock::Rel) -> bool {
126+
if self.is_disabled() {
133127
return true;
134128
}
135129

136-
/* If nSequence encodes a relative lock-time, this mask is
137-
* applied to extract that lock-time from the sequence field. */
138-
const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff;
139-
140-
let mask = SEQUENCE_LOCKTIME_MASK | SEQUENCE_LOCKTIME_TYPE_FLAG;
141-
let masked_n = n & mask;
142-
let masked_seq = self.0 & mask;
143-
if masked_n < SEQUENCE_LOCKTIME_TYPE_FLAG && masked_seq >= SEQUENCE_LOCKTIME_TYPE_FLAG {
144-
false
145-
} else {
146-
masked_n <= masked_seq
130+
if !self.is_same_type(n) {
131+
return false;
147132
}
133+
134+
n.value() <= self.value()
148135
}
149136
}
150137

151-
/// Newtype around `u32` which implements `Satisfier` using `n` as an
152-
/// absolute locktime
153-
pub struct After(pub u32);
154-
155-
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
156-
fn check_after(&self, n: u32) -> bool {
157-
// if n > self.0; we will be returning false anyways
158-
if n < LOCKTIME_THRESHOLD && self.0 >= LOCKTIME_THRESHOLD {
159-
false
160-
} else {
161-
n <= self.0
162-
}
138+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for timelock::Abs {
139+
fn check_after(&self, n: timelock::Abs) -> bool {
140+
n.is_expired(self.to_u32()).ok().unwrap_or(false)
163141
}
164142
}
165143

@@ -273,11 +251,11 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
273251
(**self).lookup_hash160(h)
274252
}
275253

276-
fn check_older(&self, t: u32) -> bool {
254+
fn check_older(&self, t: timelock::Rel) -> bool {
277255
(**self).check_older(t)
278256
}
279257

280-
fn check_after(&self, t: u32) -> bool {
258+
fn check_after(&self, t: timelock::Abs) -> bool {
281259
(**self).check_after(t)
282260
}
283261
}
@@ -335,11 +313,11 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
335313
(**self).lookup_hash160(h)
336314
}
337315

338-
fn check_older(&self, t: u32) -> bool {
316+
fn check_older(&self, t: timelock::Rel) -> bool {
339317
(**self).check_older(t)
340318
}
341319

342-
fn check_after(&self, t: u32) -> bool {
320+
fn check_after(&self, t: timelock::Abs) -> bool {
343321
(**self).check_after(t)
344322
}
345323
}
@@ -473,7 +451,7 @@ macro_rules! impl_tuple_satisfier {
473451
None
474452
}
475453

476-
fn check_older(&self, n: u32) -> bool {
454+
fn check_older(&self, n: timelock::Rel) -> bool {
477455
let &($(ref $ty,)*) = self;
478456
$(
479457
if $ty.check_older(n) {
@@ -483,7 +461,7 @@ macro_rules! impl_tuple_satisfier {
483461
false
484462
}
485463

486-
fn check_after(&self, n: u32) -> bool {
464+
fn check_after(&self, n: timelock::Abs) -> bool {
487465
let &($(ref $ty,)*) = self;
488466
$(
489467
if $ty.check_after(n) {

src/miniscript/types/correctness.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
//! Correctness/Soundness type properties
1616
17+
use bitcoin::timelock;
18+
1719
use super::{ErrorKind, Property};
1820

1921
/// Basic type representing where the fragment can go
@@ -187,6 +189,10 @@ impl Property for Correctness {
187189
}
188190
}
189191

192+
fn from_older(_: timelock::Rel) -> Self {
193+
Self::from_time(0) // arg is not used.
194+
}
195+
190196
fn cast_alt(self) -> Result<Self, ErrorKind> {
191197
Ok(Correctness {
192198
base: match self.base {

0 commit comments

Comments
 (0)