Skip to content

Commit 27e72b0

Browse files
committed
Use timelocks instead of u32
Add a new `timelock` module and implement after/older in terms of the new `Abs`/`Rel` timelocks respectively.
1 parent 2cd5257 commit 27e72b0

File tree

16 files changed

+261
-243
lines changed

16 files changed

+261
-243
lines changed

src/interpreter/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,15 +605,17 @@ where
605605
Terminal::After(ref n) => {
606606
debug_assert_eq!(node_state.n_evaluated, 0);
607607
debug_assert_eq!(node_state.n_satisfied, 0);
608-
let res = self.stack.evaluate_after(n, self.age);
608+
let res = self.stack.evaluate_after(n.as_u32(), self.age);
609609
if res.is_some() {
610610
return res;
611611
}
612612
}
613613
Terminal::Older(ref n) => {
614614
debug_assert_eq!(node_state.n_evaluated, 0);
615615
debug_assert_eq!(node_state.n_satisfied, 0);
616-
let res = self.stack.evaluate_older(n, self.height);
616+
// TODO: Improve the API to handle this use case.
617+
let n = n.to_u16() as u32;
618+
let res = self.stack.evaluate_older(&n, self.height);
617619
if res.is_some() {
618620
return res;
619621
}

src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ pub enum Error {
576576
TrNoScriptCode,
577577
/// No explicit script for Tr descriptors
578578
TrNoExplicitScript,
579+
/// Timelock error
580+
Timelock(miniscript::timelock::Error),
579581
}
580582

581583
#[doc(hidden)]
@@ -624,6 +626,13 @@ impl From<bitcoin::util::address::Error> for Error {
624626
}
625627
}
626628

629+
#[doc(hidden)]
630+
impl From<miniscript::timelock::Error> for Error {
631+
fn from(e: miniscript::timelock::Error) -> Error {
632+
Error::Timelock(e)
633+
}
634+
}
635+
627636
fn errstr(s: &str) -> Error {
628637
Error::Unexpected(s.to_owned())
629638
}
@@ -716,6 +725,7 @@ impl fmt::Display for Error {
716725
Error::TrNoExplicitScript => {
717726
write!(f, "No script code for Tr descriptors")
718727
}
728+
Error::Timelock(ref e) => e.fmt(f),
719729
}
720730
}
721731
}

src/miniscript/astelem.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
2929

3030
use crate::miniscript::context::SigType;
3131
use crate::miniscript::types::{self, Property};
32-
use crate::miniscript::ScriptContext;
32+
use crate::miniscript::{timelock, ScriptContext};
3333
use crate::util::MsKeyBuilder;
3434
use crate::{
3535
errstr, expression, script_num_size, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey,
@@ -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(timelock::Abs::from(x)))
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(timelock::Rel::from(x)))
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_u16() 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_u16() 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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::miniscript::lex::{Token as Tk, TokenIter};
2828
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
2929
use crate::miniscript::types::extra_props::ExtData;
3030
use crate::miniscript::types::{Property, Type};
31-
use crate::miniscript::ScriptContext;
31+
use crate::miniscript::{timelock, ScriptContext};
3232
use crate::{bitcoin, Error, Miniscript, MiniscriptKey, ToPublicKey};
3333

3434
fn return_none<T>(_: usize) -> Option<T> {
@@ -133,9 +133,9 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
133133
PkH(Pk::Hash),
134134
// timelocks
135135
/// `n CHECKLOCKTIMEVERIFY`
136-
After(u32),
136+
After(timelock::Abs),
137137
/// `n CHECKSEQUENCEVERIFY`
138-
Older(u32),
138+
Older(timelock::Rel),
139139
// hashlocks
140140
/// `SIZE 32 EQUALVERIFY SHA256 <hash> EQUAL`
141141
Sha256(sha256::Hash),
@@ -386,9 +386,9 @@ pub fn parse<Ctx: ScriptContext>(
386386
},
387387
// timelocks
388388
Tk::CheckSequenceVerify, Tk::Num(n)
389-
=> term.reduce0(Terminal::Older(n))?,
389+
=> term.reduce0(Terminal::Older(timelock::Rel::from(n)))?,
390390
Tk::CheckLockTimeVerify, Tk::Num(n)
391-
=> term.reduce0(Terminal::After(n))?,
391+
=> term.reduce0(Terminal::After(timelock::Abs::from(n)))?,
392392
// hashlocks
393393
Tk::Equal => match_token!(
394394
tokens,

src/miniscript/limits.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ pub const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
3434
// https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
3535
pub const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31;
3636

37+
/// Granularity for time-based relative lock-time is fixed at 512 seconds, equivalent to 2^9.
38+
// https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
39+
pub const SEQUENCE_LOCKTIME_GRANULARITY: u32 = 9;
40+
3741
/// Maximum script element size allowed by consensus rules
3842
// https://github.com/bitcoin/bitcoin/blob/42b66a6b814bca130a9ccf0a3f747cf33d628232/src/script/script.h#L23
3943
pub const MAX_SCRIPT_ELEMENT_SIZE: usize = 520;

src/miniscript/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod iter;
4040
pub mod lex;
4141
pub mod limits;
4242
pub mod satisfy;
43+
pub mod timelock;
4344
pub mod types;
4445

4546
use std::cmp;

src/miniscript/satisfy.rs

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2727
use bitcoin::secp256k1::XOnlyPublicKey;
2828
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash};
2929

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

@@ -110,56 +108,40 @@ 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_block_based() && n.is_time_based() || self.is_time_based() && n.is_block_based()
131+
{
132+
return false;
147133
}
134+
135+
n.to_u16() <= self.to_u16()
148136
}
149137
}
150138

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
139+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for timelock::Abs {
140+
fn check_after(&self, n: timelock::Abs) -> bool {
141+
if self.is_time() && n.is_height() || self.is_height() && n.is_time() {
142+
return false;
162143
}
144+
n.to_u32() <= self.to_u32()
163145
}
164146
}
165147

@@ -273,11 +255,11 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
273255
(**self).lookup_hash160(h)
274256
}
275257

276-
fn check_older(&self, t: u32) -> bool {
258+
fn check_older(&self, t: timelock::Rel) -> bool {
277259
(**self).check_older(t)
278260
}
279261

280-
fn check_after(&self, t: u32) -> bool {
262+
fn check_after(&self, t: timelock::Abs) -> bool {
281263
(**self).check_after(t)
282264
}
283265
}
@@ -335,11 +317,11 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
335317
(**self).lookup_hash160(h)
336318
}
337319

338-
fn check_older(&self, t: u32) -> bool {
320+
fn check_older(&self, t: timelock::Rel) -> bool {
339321
(**self).check_older(t)
340322
}
341323

342-
fn check_after(&self, t: u32) -> bool {
324+
fn check_after(&self, t: timelock::Abs) -> bool {
343325
(**self).check_after(t)
344326
}
345327
}
@@ -473,7 +455,7 @@ macro_rules! impl_tuple_satisfier {
473455
None
474456
}
475457

476-
fn check_older(&self, n: u32) -> bool {
458+
fn check_older(&self, n: timelock::Rel) -> bool {
477459
let &($(ref $ty,)*) = self;
478460
$(
479461
if $ty.check_older(n) {
@@ -483,7 +465,7 @@ macro_rules! impl_tuple_satisfier {
483465
false
484466
}
485467

486-
fn check_after(&self, n: u32) -> bool {
468+
fn check_after(&self, n: timelock::Abs) -> bool {
487469
let &($(ref $ty,)*) = self;
488470
$(
489471
if $ty.check_after(n) {

0 commit comments

Comments
 (0)