Skip to content

Commit 2b4a71e

Browse files
committed
WIP: Demonstrate usage of LockTime
1 parent 8fa8cdc commit 2b4a71e

File tree

18 files changed

+99
-109
lines changed

18 files changed

+99
-109
lines changed

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@ no-std = ["hashbrown", "bitcoin/no-std"]
1717
compiler = []
1818
trace = []
1919
unstable = []
20-
use-serde = ["serde", "bitcoin/use-serde"]
20+
use-serde = ["serde", "bitcoin/serde"]
2121
rand = ["bitcoin/rand"]
2222

2323
[dependencies]
24-
bitcoin = { version = "0.28.1", default-features = false }
25-
serde = { version = "1.0", optional = true }
24+
bitcoin = { git = "https://github.com/tcharding/rust-bitcoin", default-features = false, branch = "05-17-timelock" }
25+
serde = { version = "1.0", optional = true}
2626
hashbrown = { version = "0.11", optional = true }
2727

2828
[dev-dependencies]
2929
bitcoind = {version = "0.26.1", features=["22_0"]}
3030
actual-rand = { package = "rand", version = "0.8.4"}
31+
bitcoin = { git = "https://github.com/tcharding/rust-bitcoin", branch = "05-17-timelock", features = ["rand"] }
3132

3233
[[example]]
3334
name = "htlc"

examples/sign_multisig.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::collections::HashMap;
1818
use std::str::FromStr;
1919

2020
use bitcoin::blockdata::witness::Witness;
21-
use bitcoin::secp256k1;
21+
use bitcoin::{secp256k1, LockTime};
2222

2323
fn main() {
2424
let mut tx = spending_transaction();
@@ -91,7 +91,7 @@ fn main() {
9191
fn spending_transaction() -> bitcoin::Transaction {
9292
bitcoin::Transaction {
9393
version: 2,
94-
lock_time: 0,
94+
lock_time: LockTime::from(0),
9595
input: vec![bitcoin::TxIn {
9696
previous_output: Default::default(),
9797
script_sig: bitcoin::Script::new(),

examples/verify_tx.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
use std::str::FromStr;
1818

19+
use bitcoin::LockTime;
1920
use bitcoin::consensus::Decodable;
2021
use bitcoin::secp256k1::{self, Secp256k1};
2122
use bitcoin::util::sighash;
@@ -34,7 +35,7 @@ fn main() {
3435
&tx.input[0].script_sig,
3536
&tx.input[0].witness,
3637
0,
37-
0,
38+
LockTime::from(0),
3839
)
3940
.unwrap();
4041

src/descriptor/tr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
262262
TaprootBuilderError::EmptyTree => {
263263
unreachable!("Taptree is a well formed tree with atleast 1 element")
264264
}
265+
_ => panic!("Unknown error, non_exhaustive catch all"),
265266
},
266267
}
267268
};

src/interpreter/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use crate::prelude::*;
2929
pub enum Error {
3030
/// Could not satisfy, absolute locktime not met
3131
AbsoluteLocktimeNotMet(u32),
32+
/// Could not satisfy, lock time values are different units
33+
AbsoluteLocktimeComparisonInvalid(u32, u32),
3234
/// Cannot Infer a taproot descriptor
3335
/// Key spends cannot infer the internal key of the descriptor
3436
/// Inferring script spends is possible, but is hidden nodes are currently
@@ -191,6 +193,7 @@ impl fmt::Display for Error {
191193
Error::VerifyFailed => {
192194
f.write_str("Expected Satisfied Boolean at stack top for VERIFY")
193195
}
196+
_ => f.write_str("Unknown error, non_exhaustive catch all"),
194197
}
195198
}
196199
}
@@ -238,6 +241,7 @@ impl error::Error for Error {
238241
Secp(e) => Some(e),
239242
SchnorrSig(e) => Some(e),
240243
SighashError(e) => Some(e),
244+
_ => None // non_exhaustive catch all.
241245
}
242246
}
243247
}

src/interpreter/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use core::str::FromStr;
2525
use bitcoin::blockdata::witness::Witness;
2626
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2727
use bitcoin::util::{sighash, taproot};
28-
use bitcoin::{self, secp256k1, TxOut};
28+
use bitcoin::{self, secp256k1, TxOut, LockTime};
2929

3030
use crate::miniscript::context::NoChecks;
3131
use crate::miniscript::ScriptContext;
@@ -49,7 +49,7 @@ pub struct Interpreter<'txin> {
4949
/// is the leaf script; for key-spends it is `None`.
5050
script_code: Option<bitcoin::Script>,
5151
age: u32,
52-
height: u32,
52+
lock_time: LockTime,
5353
}
5454

5555
// A type representing functions for checking signatures that accept both
@@ -170,15 +170,15 @@ impl<'txin> Interpreter<'txin> {
170170
script_sig: &'txin bitcoin::Script,
171171
witness: &'txin Witness,
172172
age: u32, // CSV, relative lock time.
173-
height: u32, // CLTV, absolute lock time.
173+
lock_time: LockTime, // CLTV, absolute lock time.
174174
) -> Result<Self, Error> {
175175
let (inner, stack, script_code) = inner::from_txdata(spk, script_sig, witness)?;
176176
Ok(Interpreter {
177177
inner,
178178
stack,
179179
script_code,
180180
age,
181-
height,
181+
lock_time,
182182
})
183183
}
184184

@@ -209,7 +209,7 @@ impl<'txin> Interpreter<'txin> {
209209
// call interpreter.iter() without mutating interpreter
210210
stack: self.stack.clone(),
211211
age: self.age,
212-
height: self.height,
212+
lock_time: self.lock_time,
213213
has_errored: false,
214214
}
215215
}
@@ -492,7 +492,7 @@ pub enum SatisfiedConstraint {
492492
///Absolute Timelock for CLTV.
493493
AbsoluteTimelock {
494494
/// The value of Absolute timelock
495-
time: u32,
495+
n: LockTime,
496496
},
497497
}
498498

@@ -528,7 +528,7 @@ pub struct Iter<'intp, 'txin: 'intp> {
528528
state: Vec<NodeEvaluationState<'intp>>,
529529
stack: Stack<'txin>,
530530
age: u32,
531-
height: u32,
531+
lock_time: LockTime,
532532
has_errored: bool,
533533
}
534534

@@ -605,7 +605,7 @@ 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.height);
608+
let res = self.stack.evaluate_after(n, self.lock_time);
609609
if res.is_some() {
610610
return res;
611611
}
@@ -1131,7 +1131,7 @@ mod tests {
11311131
n_satisfied: 0,
11321132
}],
11331133
age: 1002,
1134-
height: 1002,
1134+
lock_time: LockTime::from(1002),
11351135
has_errored: false,
11361136
}
11371137
}
@@ -1197,7 +1197,7 @@ mod tests {
11971197
let after_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
11981198
assert_eq!(
11991199
after_satisfied.unwrap(),
1200-
vec![SatisfiedConstraint::AbsoluteTimelock { time: 1000 }]
1200+
vec![SatisfiedConstraint::AbsoluteTimelock { time: LockTime::from(1000) }]
12011201
);
12021202

12031203
//Check Older

src/interpreter/stack.rs

Lines changed: 10 additions & 7 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::{locktime, LockTime};
2021

2122
use super::error::PkEvalErrInner;
2223
use super::{
@@ -231,14 +232,16 @@ impl<'txin> Stack<'txin> {
231232
/// booleans
232233
pub(super) fn evaluate_after(
233234
&mut self,
234-
n: &u32,
235-
lock_time: u32,
235+
n: &LockTime,
236+
lock_time: LockTime,
236237
) -> Option<Result<SatisfiedConstraint, Error>> {
237-
if lock_time >= *n {
238-
self.push(Element::Satisfied);
239-
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { time: *n }))
240-
} else {
241-
Some(Err(Error::AbsoluteLocktimeNotMet(*n)))
238+
match locktime::is_satisfied(*n, lock_time) {
239+
Ok(true) => {
240+
self.push(Element::Satisfied);
241+
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { n: *n }))
242+
},
243+
Ok(false) => Some(Err(Error::AbsoluteLocktimeNotMet(n.to_u32()))),
244+
Err(_) => Some(Err(Error::AbsoluteLocktimeComparisonInvalid(n.to_u32(), lock_time.to_u32()))),
242245
}
243246
}
244247

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ pub mod interpreter;
116116
pub mod miniscript;
117117
pub mod policy;
118118
pub mod psbt;
119-
pub mod timelock;
120119

121120
mod util;
122121

src/miniscript/astelem.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use core::str::FromStr;
2323
use core::{fmt, str};
2424

25+
use bitcoin::LockTime;
2526
use bitcoin::blockdata::{opcodes, script};
2627
use bitcoin::hashes::hex::FromHex;
2728
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
@@ -492,7 +493,7 @@ where
492493
expression::terminal(&top.args[0], |x| Pk::Hash::from_str(x).map(Terminal::PkH))
493494
}
494495
("after", 1) => expression::terminal(&top.args[0], |x| {
495-
expression::parse_num(x).map(Terminal::After)
496+
expression::parse_num(x).map(|x| Terminal::After(LockTime::from(x)))
496497
}),
497498
("older", 1) => expression::terminal(&top.args[0], |x| {
498499
expression::parse_num(x).map(Terminal::Older)
@@ -654,7 +655,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
654655
.push_slice(&Pk::hash_to_hash160(hash)[..])
655656
.push_opcode(opcodes::all::OP_EQUALVERIFY),
656657
Terminal::After(t) => builder
657-
.push_int(t as i64)
658+
.push_int(t.to_u32() as i64)
658659
.push_opcode(opcodes::all::OP_CLTV),
659660
Terminal::Older(t) => builder.push_int(t as i64).push_opcode(opcodes::all::OP_CSV),
660661
Terminal::Sha256(h) => builder
@@ -789,7 +790,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
789790
match *self {
790791
Terminal::PkK(ref pk) => Ctx::pk_len(pk),
791792
Terminal::PkH(..) => 24,
792-
Terminal::After(n) => script_num_size(n as usize) + 1,
793+
Terminal::After(n) => script_num_size(n.to_u32() as usize) + 1,
793794
Terminal::Older(n) => script_num_size(n as usize) + 1,
794795
Terminal::Sha256(..) => 33 + 6,
795796
Terminal::Hash256(..) => 33 + 6,

src/miniscript/decode.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use sync::Arc;
2828

2929
use crate::miniscript::lex::{Token as Tk, TokenIter};
3030
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
31+
use crate::bitcoin::locktime::LockTime;
3132
use crate::miniscript::types::extra_props::ExtData;
3233
use crate::miniscript::types::{Property, Type};
3334
use crate::miniscript::ScriptContext;
@@ -137,7 +138,7 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
137138
PkH(Pk::Hash),
138139
// timelocks
139140
/// `n CHECKLOCKTIMEVERIFY`
140-
After(u32),
141+
After(LockTime),
141142
/// `n CHECKSEQUENCEVERIFY`
142143
Older(u32),
143144
// hashlocks
@@ -392,7 +393,7 @@ pub fn parse<Ctx: ScriptContext>(
392393
Tk::CheckSequenceVerify, Tk::Num(n)
393394
=> term.reduce0(Terminal::Older(n))?,
394395
Tk::CheckLockTimeVerify, Tk::Num(n)
395-
=> term.reduce0(Terminal::After(n))?,
396+
=> term.reduce0(Terminal::After(LockTime::from(n)))?,
396397
// hashlocks
397398
Tk::Equal => match_token!(
398399
tokens,

src/miniscript/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ mod tests {
981981
));
982982
assert_eq!(
983983
ms.unwrap_err().to_string(),
984-
"unexpected «Key hex decoding error: bad hex string length 64 (expected 66)»"
984+
"unexpected «key hex decoding error»"
985985
);
986986
Tapscript::from_str_insane(&format!(
987987
"pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"

src/miniscript/satisfy.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
2121
use core::{cmp, i64, mem};
2222

23-
use bitcoin;
23+
use bitcoin::LockTime;
2424
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2525
use bitcoin::secp256k1::XOnlyPublicKey;
2626
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash};
2727
use sync::Arc;
2828

2929
use crate::miniscript::limits::{
30-
LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
30+
SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
3131
};
3232
use crate::prelude::*;
3333
use crate::util::witness_size;
@@ -115,7 +115,7 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
115115
}
116116

117117
/// Assert whether a absolute locktime is satisfied
118-
fn check_after(&self, _: u32) -> bool {
118+
fn check_after(&self, _: LockTime) -> bool {
119119
false
120120
}
121121
}
@@ -148,21 +148,6 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
148148
}
149149
}
150150

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-
}
163-
}
164-
}
165-
166151
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk, bitcoin::EcdsaSig> {
167152
fn lookup_ecdsa_sig(&self, key: &Pk) -> Option<bitcoin::EcdsaSig> {
168153
self.get(key).copied()
@@ -277,7 +262,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
277262
(**self).check_older(t)
278263
}
279264

280-
fn check_after(&self, t: u32) -> bool {
265+
fn check_after(&self, t: LockTime) -> bool {
281266
(**self).check_after(t)
282267
}
283268
}
@@ -339,7 +324,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
339324
(**self).check_older(t)
340325
}
341326

342-
fn check_after(&self, t: u32) -> bool {
327+
fn check_after(&self, t: LockTime) -> bool {
343328
(**self).check_after(t)
344329
}
345330
}
@@ -483,7 +468,7 @@ macro_rules! impl_tuple_satisfier {
483468
false
484469
}
485470

486-
fn check_after(&self, n: u32) -> bool {
471+
fn check_after(&self, n: LockTime) -> bool {
487472
let &($(ref $ty,)*) = self;
488473
$(
489474
if $ty.check_after(n) {

0 commit comments

Comments
 (0)