Skip to content

Commit

Permalink
wip: Use LockTime
Browse files Browse the repository at this point in the history
  • Loading branch information
tcharding committed Jun 3, 2022
1 parent 0ac479f commit 2f1ae91
Show file tree
Hide file tree
Showing 18 changed files with 98 additions and 106 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Expand Up @@ -15,17 +15,17 @@ compiler = []
trace = []
unstable = []
default = []
use-serde = ["serde", "bitcoin/use-serde"]
use-serde = ["serde", "bitcoin/serde"]
rand = ["bitcoin/rand"]

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

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

[[example]]
name = "htlc"
Expand Down
4 changes: 2 additions & 2 deletions examples/sign_multisig.rs
Expand Up @@ -18,7 +18,7 @@ use std::collections::HashMap;
use std::str::FromStr;

use bitcoin::blockdata::witness::Witness;
use bitcoin::secp256k1;
use bitcoin::{secp256k1, LockTime};

fn main() {
let mut tx = spending_transaction();
Expand Down Expand Up @@ -91,7 +91,7 @@ fn main() {
fn spending_transaction() -> bitcoin::Transaction {
bitcoin::Transaction {
version: 2,
lock_time: 0,
lock_time: LockTime::from(0),
input: vec![bitcoin::TxIn {
previous_output: Default::default(),
script_sig: bitcoin::Script::new(),
Expand Down
3 changes: 2 additions & 1 deletion examples/verify_tx.rs
Expand Up @@ -16,6 +16,7 @@

use std::str::FromStr;

use bitcoin::LockTime;
use bitcoin::consensus::Decodable;
use bitcoin::secp256k1::{self, Secp256k1};
use bitcoin::util::sighash;
Expand All @@ -33,7 +34,7 @@ fn main() {
&spk_input_1,
&tx.input[0].script_sig,
&tx.input[0].witness,
0,
LockTime::from(0),
0,
)
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/descriptor/tr.rs
Expand Up @@ -262,6 +262,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
TaprootBuilderError::EmptyTree => {
unreachable!("Taptree is a well formed tree with atleast 1 element")
}
_ => panic!("Unknown error, non_exhaustive catch all"),
},
}
};
Expand Down
4 changes: 4 additions & 0 deletions src/interpreter/error.rs
Expand Up @@ -26,6 +26,8 @@ use super::BitcoinKey;
pub enum Error {
/// Could not satisfy, absolute locktime not met
AbsoluteLocktimeNotMet(u32),
/// Could not satisfy, lock time values are different units
AbsoluteLocktimeComparisonInvalid(u32, u32),
/// Cannot Infer a taproot descriptor
/// Key spends cannot infer the internal key of the descriptor
/// Inferring script spends is possible, but is hidden nodes are currently
Expand Down Expand Up @@ -188,6 +190,7 @@ impl fmt::Display for Error {
Error::VerifyFailed => {
f.write_str("Expected Satisfied Boolean at stack top for VERIFY")
}
_ => f.write_str("Unknown error, non_exhaustive catch all"),
}
}
}
Expand Down Expand Up @@ -234,6 +237,7 @@ impl error::Error for Error {
Secp(e) => Some(e),
SchnorrSig(e) => Some(e),
SighashError(e) => Some(e),
_ => None // non_exhaustive catch all.
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/interpreter/mod.rs
Expand Up @@ -26,7 +26,7 @@ use std::str::FromStr;
use bitcoin::blockdata::witness::Witness;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::util::{sighash, taproot};
use bitcoin::{self, secp256k1, TxOut};
use bitcoin::{self, secp256k1, TxOut, LockTime};

use crate::miniscript::context::NoChecks;
use crate::miniscript::ScriptContext;
Expand All @@ -48,7 +48,7 @@ pub struct Interpreter<'txin> {
/// For non-Taproot spends, the scriptCode; for Taproot script-spends, this
/// is the leaf script; for key-spends it is `None`.
script_code: Option<bitcoin::Script>,
age: u32,
age: LockTime,
height: u32,
}

Expand Down Expand Up @@ -169,7 +169,7 @@ impl<'txin> Interpreter<'txin> {
spk: &bitcoin::Script,
script_sig: &'txin bitcoin::Script,
witness: &'txin Witness,
age: u32,
age: LockTime,
height: u32,
) -> Result<Self, Error> {
let (inner, stack, script_code) = inner::from_txdata(spk, script_sig, witness)?;
Expand Down Expand Up @@ -492,7 +492,7 @@ pub enum SatisfiedConstraint {
///Absolute Timelock for CLTV.
AbsoluteTimelock {
/// The value of Absolute timelock
time: u32,
time: LockTime,
},
}

Expand Down Expand Up @@ -527,7 +527,7 @@ pub struct Iter<'intp, 'txin: 'intp> {
public_key: Option<&'intp BitcoinKey>,
state: Vec<NodeEvaluationState<'intp>>,
stack: Stack<'txin>,
age: u32,
age: LockTime,
height: u32,
has_errored: bool,
}
Expand Down Expand Up @@ -1130,7 +1130,7 @@ mod tests {
n_evaluated: 0,
n_satisfied: 0,
}],
age: 1002,
age: LockTime::from(1002),
height: 1002,
has_errored: false,
}
Expand Down Expand Up @@ -1197,7 +1197,7 @@ mod tests {
let after_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
after_satisfied.unwrap(),
vec![SatisfiedConstraint::AbsoluteTimelock { time: 1000 }]
vec![SatisfiedConstraint::AbsoluteTimelock { time: LockTime::from(1000) }]
);

//Check Older
Expand Down
17 changes: 10 additions & 7 deletions src/interpreter/stack.rs
Expand Up @@ -17,6 +17,7 @@
use bitcoin;
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::{locktime, LockTime};

use super::error::PkEvalErrInner;
use super::{
Expand Down Expand Up @@ -230,14 +231,16 @@ impl<'txin> Stack<'txin> {
/// booleans
pub(super) fn evaluate_after(
&mut self,
n: &u32,
age: u32,
n: &LockTime,
age: LockTime,
) -> Option<Result<SatisfiedConstraint, Error>> {
if age >= *n {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { time: *n }))
} else {
Some(Err(Error::AbsoluteLocktimeNotMet(*n)))
match locktime::is_satisfied(*n, age) {
Ok(true) => {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { time: *n }))
},
Ok(false) => Some(Err(Error::AbsoluteLocktimeNotMet(n.to_u32()))),
Err(_) => Some(Err(Error::AbsoluteLocktimeComparisonInvalid(n.to_u32(), age.to_u32()))),
}
}

Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Expand Up @@ -104,7 +104,6 @@ pub mod interpreter;
pub mod miniscript;
pub mod policy;
pub mod psbt;
pub mod timelock;

mod util;

Expand Down
7 changes: 4 additions & 3 deletions src/miniscript/astelem.rs
Expand Up @@ -23,6 +23,7 @@ use std::str::FromStr;
use std::sync::Arc;
use std::{fmt, str};

use bitcoin::LockTime;
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
Expand Down Expand Up @@ -491,7 +492,7 @@ where
expression::terminal(&top.args[0], |x| Pk::Hash::from_str(x).map(Terminal::PkH))
}
("after", 1) => expression::terminal(&top.args[0], |x| {
expression::parse_num(x).map(Terminal::After)
expression::parse_num(x).map(|x| Terminal::After(LockTime::from(x)))
}),
("older", 1) => expression::terminal(&top.args[0], |x| {
expression::parse_num(x).map(Terminal::Older)
Expand Down Expand Up @@ -653,7 +654,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
.push_slice(&Pk::hash_to_hash160(hash)[..])
.push_opcode(opcodes::all::OP_EQUALVERIFY),
Terminal::After(t) => builder
.push_int(t as i64)
.push_int(t.to_u32() as i64)
.push_opcode(opcodes::all::OP_CLTV),
Terminal::Older(t) => builder.push_int(t as i64).push_opcode(opcodes::all::OP_CSV),
Terminal::Sha256(h) => builder
Expand Down Expand Up @@ -788,7 +789,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
match *self {
Terminal::PkK(ref pk) => Ctx::pk_len(pk),
Terminal::PkH(..) => 24,
Terminal::After(n) => script_num_size(n as usize) + 1,
Terminal::After(n) => script_num_size(n.to_u32() as usize) + 1,
Terminal::Older(n) => script_num_size(n as usize) + 1,
Terminal::Sha256(..) => 33 + 6,
Terminal::Hash256(..) => 33 + 6,
Expand Down
5 changes: 3 additions & 2 deletions src/miniscript/decode.rs
Expand Up @@ -26,6 +26,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};

use crate::miniscript::lex::{Token as Tk, TokenIter};
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
use crate::bitcoin::locktime::LockTime;
use crate::miniscript::types::extra_props::ExtData;
use crate::miniscript::types::{Property, Type};
use crate::miniscript::ScriptContext;
Expand Down Expand Up @@ -133,7 +134,7 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
PkH(Pk::Hash),
// timelocks
/// `n CHECKLOCKTIMEVERIFY`
After(u32),
After(LockTime),
/// `n CHECKSEQUENCEVERIFY`
Older(u32),
// hashlocks
Expand Down Expand Up @@ -388,7 +389,7 @@ pub fn parse<Ctx: ScriptContext>(
Tk::CheckSequenceVerify, Tk::Num(n)
=> term.reduce0(Terminal::Older(n))?,
Tk::CheckLockTimeVerify, Tk::Num(n)
=> term.reduce0(Terminal::After(n))?,
=> term.reduce0(Terminal::After(LockTime::from(n)))?,
// hashlocks
Tk::Equal => match_token!(
tokens,
Expand Down
2 changes: 1 addition & 1 deletion src/miniscript/mod.rs
Expand Up @@ -978,7 +978,7 @@ mod tests {
));
assert_eq!(
ms.unwrap_err().to_string(),
"unexpected «Key hex decoding error: bad hex string length 64 (expected 66)»"
"unexpected «key hex decoding error»"
);
Tapscript::from_str_insane(&format!(
"pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
Expand Down
27 changes: 6 additions & 21 deletions src/miniscript/satisfy.rs
Expand Up @@ -22,13 +22,13 @@ use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use std::{cmp, i64, mem};

use bitcoin;
use bitcoin::LockTime;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::secp256k1::XOnlyPublicKey;
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash};

use crate::miniscript::limits::{
LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
};
use crate::util::witness_size;
use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
Expand Down Expand Up @@ -115,7 +115,7 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
}

/// Assert whether a absolute locktime is satisfied
fn check_after(&self, _: u32) -> bool {
fn check_after(&self, _: LockTime) -> bool {
false
}
}
Expand Down Expand Up @@ -148,21 +148,6 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
}
}

/// Newtype around `u32` which implements `Satisfier` using `n` as an
/// absolute locktime
pub struct After(pub u32);

impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
fn check_after(&self, n: u32) -> bool {
// if n > self.0; we will be returning false anyways
if n < LOCKTIME_THRESHOLD && self.0 >= LOCKTIME_THRESHOLD {
false
} else {
n <= self.0
}
}
}

impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk, bitcoin::EcdsaSig> {
fn lookup_ecdsa_sig(&self, key: &Pk) -> Option<bitcoin::EcdsaSig> {
self.get(key).copied()
Expand Down Expand Up @@ -277,7 +262,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
(**self).check_older(t)
}

fn check_after(&self, t: u32) -> bool {
fn check_after(&self, t: LockTime) -> bool {
(**self).check_after(t)
}
}
Expand Down Expand Up @@ -339,7 +324,7 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
(**self).check_older(t)
}

fn check_after(&self, t: u32) -> bool {
fn check_after(&self, t: LockTime) -> bool {
(**self).check_after(t)
}
}
Expand Down Expand Up @@ -483,7 +468,7 @@ macro_rules! impl_tuple_satisfier {
false
}

fn check_after(&self, n: u32) -> bool {
fn check_after(&self, n: LockTime) -> bool {
let &($(ref $ty,)*) = self;
$(
if $ty.check_after(n) {
Expand Down
14 changes: 8 additions & 6 deletions src/miniscript/types/extra_props.rs
Expand Up @@ -4,10 +4,12 @@
use std::cmp;
use std::iter::once;

use bitcoin::LockTime;

use super::{Error, ErrorKind, Property, ScriptContext};
use crate::miniscript::context::SigType;
use crate::miniscript::limits::{
LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
};
use crate::{script_num_size, MiniscriptKey, Terminal};

Expand Down Expand Up @@ -337,9 +339,9 @@ impl Property for ExtData {
unreachable!()
}

fn from_after(t: u32) -> Self {
fn from_after(t: LockTime) -> Self {
ExtData {
pk_cost: script_num_size(t as usize) + 1,
pk_cost: script_num_size(t.to_u32() as usize) + 1,
has_free_verify: false,
ops: OpLimits::new(1, Some(0), None),
stack_elem_count_sat: Some(0),
Expand All @@ -349,8 +351,8 @@ impl Property for ExtData {
timelock_info: TimelockInfo {
csv_with_height: false,
csv_with_time: false,
cltv_with_height: t < LOCKTIME_THRESHOLD,
cltv_with_time: t >= LOCKTIME_THRESHOLD,
cltv_with_height: t.is_height(),
cltv_with_time: t.is_time(),
contains_combination: false,
},
exec_stack_elem_count_sat: Some(1), // <t>
Expand Down Expand Up @@ -933,7 +935,7 @@ impl Property for ExtData {
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
// only consumes 4 bytes from the stack.
if t == 0 || (t & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0 {
if t.to_u32() == 0 || (t.to_u32() & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::InvalidTime,
Expand Down

0 comments on commit 2f1ae91

Please sign in to comment.