Merge rust-bitcoin/rust-bitcoin#1196: Add relative lock time type

da95c3a489 Add newtype `relative::LockTime` (Tobin C. Harding)
cbfa8cff4c Improve lock time docs (Tobin C. Harding)
9f7d934f5d Move the locktime module to absolute (Tobin C. Harding)

Pull request description:

  Patches 1 and 2 are preparation. Patch 3 adds a new `relative::LockTime` type.

  Please see https://github.com/rust-bitcoin/rust-miniscript/pull/455 for demo of use of the new API in miniscript.

  ### Some links to help during review
  - https://medium.com/summa-technology/bitcoins-time-locks-27e0c362d7a1
  - https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
  - https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki

ACKs for top commit:
  apoelstra:
    ACK da95c3a489
  sanket1729:
    ACK da95c3a489. Left a minor nit

Tree-SHA512: 00137161eee354faa803f74dd33dbaaf58ced58cf54d334200be28e10281bb98cb72dff38b3205d1741b8d42100c3b8229e4cfa2155e46bb74b418b612c12dec
This commit is contained in:
Andrew Poelstra 2022-09-01 14:49:19 +00:00
commit 7efe30c5e8
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
11 changed files with 387 additions and 85 deletions

View File

@ -34,6 +34,7 @@ use std::str::FromStr;
use bitcoin::consensus::encode; use bitcoin::consensus::encode;
use bitcoin::hashes::hex::{self, FromHex}; use bitcoin::hashes::hex::{self, FromHex};
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
use bitcoin::util::amount::ParseAmountError; use bitcoin::util::amount::ParseAmountError;
use bitcoin::util::bip32::{ use bitcoin::util::bip32::{
@ -42,7 +43,7 @@ use bitcoin::util::bip32::{
}; };
use bitcoin::util::psbt::{self, Input, Psbt, PsbtSighashType}; use bitcoin::util::psbt::{self, Input, Psbt, PsbtSighashType};
use bitcoin::{ use bitcoin::{
address, Address, Amount, Network, OutPoint, PackedLockTime, PrivateKey, PublicKey, Script, address, Address, Amount, Network, OutPoint, PrivateKey, PublicKey, Script,
Sequence, Transaction, TxIn, TxOut, Txid, Witness, Sequence, Transaction, TxIn, TxOut, Txid, Witness,
}; };
@ -206,7 +207,7 @@ impl WatchOnly {
let tx = Transaction { let tx = Transaction {
version: 2, version: 2,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex(INPUT_UTXO_TXID)?, txid: Txid::from_hex(INPUT_UTXO_TXID)?,

View File

@ -16,7 +16,7 @@ use crate::hashes::hex::{self, HexIterator};
use crate::hashes::{Hash, sha256d}; use crate::hashes::{Hash, sha256d};
use crate::blockdata::opcodes; use crate::blockdata::opcodes;
use crate::blockdata::script; use crate::blockdata::script;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence}; use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence};
use crate::blockdata::block::{Block, BlockHeader}; use crate::blockdata::block::{Block, BlockHeader};
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
@ -72,7 +72,7 @@ fn bitcoin_genesis_tx() -> Transaction {
// Base // Base
let mut ret = Transaction { let mut ret = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![], input: vec![],
output: vec![], output: vec![],
}; };
@ -200,7 +200,7 @@ mod test {
use crate::hashes::hex::{ToHex, FromHex}; use crate::hashes::hex::{ToHex, FromHex};
use crate::network::constants::Network; use crate::network::constants::Network;
use crate::consensus::encode::serialize; use crate::consensus::encode::serialize;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
#[test] #[test]
fn bitcoin_genesis_first_transaction() { fn bitcoin_genesis_first_transaction() {
@ -218,7 +218,7 @@ mod test {
assert_eq!(serialize(&gen.output[0].script_pubkey), assert_eq!(serialize(&gen.output[0].script_pubkey),
Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap()); Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap());
assert_eq!(gen.output[0].value, 50 * COIN_VALUE); assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
assert_eq!(gen.lock_time, PackedLockTime::ZERO); assert_eq!(gen.lock_time, absolute::PackedLockTime::ZERO);
assert_eq!(gen.wtxid().to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); assert_eq!(gen.wtxid().to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
} }

View File

@ -47,7 +47,8 @@ pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// # use bitcoin::{Amount, PackedLockTime, LockTime}; /// # use bitcoin::Amount;
/// # use bitcoin::absolute::{PackedLockTime, LockTime};
/// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] /// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
/// struct S { /// struct S {
/// lock_time: PackedLockTime, /// lock_time: PackedLockTime,
@ -140,7 +141,8 @@ impl fmt::UpperHex for PackedLockTime {
} }
} }
/// A lock time value, representing either a block height or a UNIX timestamp (seconds since epoch). /// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds
/// since epoch).
/// ///
/// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`crate::Transaction::lock_time`] /// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`crate::Transaction::lock_time`]
/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`. /// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`.
@ -152,10 +154,10 @@ impl fmt::UpperHex for PackedLockTime {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// # use bitcoin::{LockTime, LockTime::*}; /// # use bitcoin::absolute::{LockTime, LockTime::*};
/// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = LockTime::from_consensus(100); // nLockTime /// # let lock_time = LockTime::from_consensus(100); // nLockTime
/// // To compare lock times there are various `is_satisfied_*` methods, you may also use: /// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
/// let is_satisfied = match (n, lock_time) { /// let is_satisfied = match (n, lock_time) {
/// (Blocks(n), Blocks(lock_time)) => n <= lock_time, /// (Blocks(n), Blocks(lock_time)) => n <= lock_time,
/// (Seconds(n), Seconds(lock_time)) => n <= lock_time, /// (Seconds(n), Seconds(lock_time)) => n <= lock_time,
@ -171,7 +173,7 @@ pub enum LockTime {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::LockTime; /// use bitcoin::absolute::LockTime;
/// ///
/// let block: u32 = 741521; /// let block: u32 = 741521;
/// let n = LockTime::from_height(block).expect("valid height"); /// let n = LockTime::from_height(block).expect("valid height");
@ -183,7 +185,7 @@ pub enum LockTime {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::LockTime; /// use bitcoin::absolute::LockTime;
/// ///
/// let seconds: u32 = 1653195600; // May 22nd, 5am UTC. /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
/// let n = LockTime::from_time(seconds).expect("valid time"); /// let n = LockTime::from_time(seconds).expect("valid time");
@ -203,7 +205,7 @@ impl LockTime {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use bitcoin::LockTime; /// # use bitcoin::absolute::LockTime;
/// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY /// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
/// ///
/// // `from_consensus` roundtrips as expected with `to_consensus_u32`. /// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
@ -225,7 +227,7 @@ impl LockTime {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # use bitcoin::LockTime; /// # use bitcoin::absolute::LockTime;
/// assert!(LockTime::from_height(741521).is_ok()); /// assert!(LockTime::from_height(741521).is_ok());
/// assert!(LockTime::from_height(1653195600).is_err()); /// assert!(LockTime::from_height(1653195600).is_err());
/// ``` /// ```
@ -241,7 +243,7 @@ impl LockTime {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # use bitcoin::LockTime; /// # use bitcoin::absolute::LockTime;
/// assert!(LockTime::from_time(1653195600).is_ok()); /// assert!(LockTime::from_time(1653195600).is_ok());
/// assert!(LockTime::from_time(741521).is_err()); /// assert!(LockTime::from_time(741521).is_err());
/// ``` /// ```
@ -283,7 +285,7 @@ impl LockTime {
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # use bitcoin::locktime::{LockTime, Height, Time}; /// # use bitcoin::absolute::{LockTime, Height, Time};
/// // Can be implemented if block chain data is available. /// // Can be implemented if block chain data is available.
/// fn get_height() -> Height { todo!("return the current block height") } /// fn get_height() -> Height { todo!("return the current block height") }
/// fn get_time() -> Time { todo!("return the current block time") } /// fn get_time() -> Time { todo!("return the current block time") }
@ -314,7 +316,7 @@ impl LockTime {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use bitcoin::{LockTime, LockTime::*}; /// # use bitcoin::absolute::{LockTime, LockTime::*};
/// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = LockTime::from_consensus(100); // nLockTime /// # let lock_time = LockTime::from_consensus(100); // nLockTime
/// ///
@ -410,7 +412,7 @@ impl Height {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::locktime::Height; /// use bitcoin::locktime::absolute::Height;
/// ///
/// let h: u32 = 741521; /// let h: u32 = 741521;
/// let height = Height::from_consensus(h).expect("invalid height value"); /// let height = Height::from_consensus(h).expect("invalid height value");
@ -429,7 +431,7 @@ impl Height {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::LockTime; /// use bitcoin::absolute::LockTime;
/// ///
/// let n_lock_time: u32 = 741521; /// let n_lock_time: u32 = 741521;
/// let lock_time = LockTime::from_consensus(n_lock_time); /// let lock_time = LockTime::from_consensus(n_lock_time);
@ -493,7 +495,7 @@ impl Time {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::locktime::Time; /// use bitcoin::locktime::absolute::Time;
/// ///
/// let t: u32 = 1653195600; // May 22nd, 5am UTC. /// let t: u32 = 1653195600; // May 22nd, 5am UTC.
/// let time = Time::from_consensus(t).expect("invalid time value"); /// let time = Time::from_consensus(t).expect("invalid time value");
@ -512,7 +514,7 @@ impl Time {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// use bitcoin::LockTime; /// use bitcoin::absolute::LockTime;
/// ///
/// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC. /// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC.
/// let lock_time = LockTime::from_consensus(n_lock_time); /// let lock_time = LockTime::from_consensus(n_lock_time);

View File

@ -0,0 +1,8 @@
// Rust Bitcoin Library - Written by the rust-bitcoin developers.
// SPDX-License-Identifier: CC0-1.0
//! Provides absolute and relative locktimes.
//!
pub mod absolute;
pub mod relative;

View File

@ -0,0 +1,289 @@
// Rust Bitcoin Library - Written by the rust-bitcoin developers.
// SPDX-License-Identifier: CC0-1.0
//! Provides type [`LockTime`] that implements the logic around nSequence/OP_CHECKSEQUENCEVERIFY.
//!
//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
//! whether bit 22 of the `u32` consensus value is set.
//!
use core::fmt;
use core::convert::TryFrom;
#[cfg(docsrs)]
use crate::relative;
/// A relative lock time value, representing either a block height or time (512 second intervals).
///
/// The `relative::LockTime` type does not have any constructors, this is by design, please use
/// `Sequence::to_relative_lock_time` to create a relative lock time.
///
/// ### Relevant BIPs
///
/// * [BIP 68 Relative lock-time using consensus-enforced sequence numbers](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
/// * [BIP 112 CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki)
#[allow(clippy::derive_ord_xor_partial_ord)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub enum LockTime {
/// A block height lock time value.
Blocks(Height),
/// A 512 second time interval value.
Time(Time),
}
impl LockTime {
/// Returns true if this [`relative::LockTime`] is satisfied by either height or time.
///
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
///
/// # let height = 100; // 100 blocks.
/// # let intervals = 70; // Approx 10 hours.
/// # let current_height = || Height::from(height + 10);
/// # let current_time = || Time::from_512_second_intervals(intervals + 10);
/// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
///
/// // Users that have chain data can get the current height and time to check against a lock.
/// let height_and_time = (current_time(), current_height()); // tuple order does not matter.
/// assert!(lock.is_satisfied_by(current_height(), current_time()));
/// ```
pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool {
if let Ok(true) = self.is_satisfied_by_height(h) {
true
} else if let Ok(true) = self.is_satisfied_by_time(t) {
true
} else {
false
}
}
/// Returns true if this [`relative::LockTime`] is satisfied by `other` lock.
///
/// This function is useful when checking sequence values against a lock, first one checks the
/// sequence represents a relative lock time by converting to `LockTime` then use this function
/// to see if [`LockTime`] is satisfied by the newly created lock.
///
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
///
/// # let height = 100; // 100 blocks.
/// # let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
/// # let test_sequence = Sequence::from_height(height + 10);
///
/// let satisfied = match test_sequence.to_relative_lock_time() {
/// None => false, // Handle non-lock-time case.
/// Some(test_lock) => lock.is_satisfied_by_lock(test_lock),
/// };
/// assert!(satisfied);
/// ```
pub fn is_satisfied_by_lock(&self, other: LockTime) -> bool {
use LockTime::*;
match (*self, other) {
(Blocks(n), Blocks(m)) => n.value() <= m.value(),
(Time(n), Time(m)) => n.value() <= m.value(),
_ => false, // Not the same units.
}
}
/// Returns true if this [`relative::LockTime`] is satisfied by [`Height`].
///
/// # Errors
///
/// Returns an error if this lock is not lock-by-height.
///
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
///
/// let height: u16 = 100;
/// let lock = Sequence::from_height(height).to_relative_lock_time().expect("valid height");
/// assert!(lock.is_satisfied_by_height(Height::from(height+1)).expect("a height"));
/// ```
#[inline]
pub fn is_satisfied_by_height(&self, h: Height) -> Result<bool, Error> {
use LockTime::*;
match *self {
Blocks(ref height) => Ok(height.value() <= h.value()),
Time(ref time) => Err(Error::IncompatibleTime(*self, *time)),
}
}
/// Returns true if this [`relative::LockTime`] is satisfied by [`Time`].
///
/// # Errors
///
/// Returns an error if this lock is not lock-by-time.
///
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
///
/// let intervals: u16 = 70; // approx 10 hours;
/// let lock = Sequence::from_512_second_intervals(intervals).to_relative_lock_time().expect("valid time");
/// assert!(lock.is_satisfied_by_time(Time::from_512_second_intervals(intervals + 10)).expect("a time"));
/// ```
#[inline]
pub fn is_satisfied_by_time(&self, t: Time) -> Result<bool, Error> {
use LockTime::*;
match *self {
Time(ref time) => Ok(time.value() <= t.value()),
Blocks(ref height) => Err(Error::IncompatibleHeight(*self, *height)),
}
}
}
impl From<Height> for LockTime {
#[inline]
fn from(h: Height) -> Self {
LockTime::Blocks(h)
}
}
impl From<Time> for LockTime {
#[inline]
fn from(t: Time) -> Self {
LockTime::Time(t)
}
}
impl fmt::Display for LockTime {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LockTime::*;
if f.alternate() {
match *self {
Blocks(ref h) => write!(f, "block-height {}", h),
Time(ref t) => write!(f, "block-time {} (512 second intervals)", t),
}
} else {
match *self {
Blocks(ref h) => fmt::Display::fmt(h, f),
Time(ref t) => fmt::Display::fmt(t, f),
}
}
}
}
/// A relative lock time lock-by-blockheight value.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Height(u16);
impl Height {
/// Returns the inner `u16` value.
#[inline]
pub fn value(self) -> u16 {
self.0
}
}
impl From<u16> for Height {
#[inline]
fn from(value: u16) -> Self {
Height(value)
}
}
impl fmt::Display for Height {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
/// A relative lock time lock-by-blocktime value.
///
/// For BIP 68 relative lock-by-blocktime locks, time is measure in 512 second intervals.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Time(u16);
impl Time {
/// Create a [`Time`] using time intervals where each interval is equivalent to 512 seconds.
///
/// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin.
#[inline]
pub fn from_512_second_intervals(intervals: u16) -> Self {
Time(intervals)
}
/// Create a [`Time`] from seconds, converting the seconds into 512 second interval with ceiling
/// division.
///
/// # Errors
///
/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, Error> {
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
Ok(Time::from_512_second_intervals(interval))
} else {
Err(Error::IntegerOverflow(seconds))
}
}
/// Returns the inner `u16` value.
#[inline]
pub fn value(self) -> u16 {
self.0
}
}
impl fmt::Display for Time {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
/// Errors related to relative lock times.
#[derive(Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum Error {
/// Input time in seconds was too large to be encoded to a 16 bit 512 second interval.
IntegerOverflow(u32),
/// Tried to satisfy a lock-by-blocktime lock using a height value.
IncompatibleHeight(LockTime, Height),
/// Tried to satisfy a lock-by-blockheight lock using a time value.
IncompatibleTime(LockTime, Time),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::IntegerOverflow(val) => write!(f, "{} seconds is too large to be encoded to a 16 bit 512 second interval", val),
Self::IncompatibleHeight(lock, height) => write!(f, "tried to satisfy lock {} with height: {}", lock, height),
Self::IncompatibleTime(lock, time) => write!(f, "tried to satisfy lock {} with time: {}", lock, time),
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use self::Error::*;
match *self {
IntegerOverflow(_) | IncompatibleHeight(_, _) | IncompatibleTime(_, _) => None,
}
}
}

View File

@ -25,7 +25,8 @@ use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
#[cfg(feature="bitcoinconsensus")] use crate::blockdata::script; #[cfg(feature="bitcoinconsensus")] use crate::blockdata::script;
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::blockdata::locktime::{LockTime, PackedLockTime, Height, Time}; use crate::blockdata::locktime::absolute::{self, Height, Time};
use crate::blockdata::locktime::relative;
use crate::consensus::{encode, Decodable, Encodable}; use crate::consensus::{encode, Decodable, Encodable};
use crate::hash_types::{Sighash, Txid, Wtxid}; use crate::hash_types::{Sighash, Txid, Wtxid};
use crate::VarInt; use crate::VarInt;
@ -207,7 +208,8 @@ pub struct TxIn {
} }
impl TxIn { impl TxIn {
/// Returns true if this input enables the [`LockTime`] (aka `nLockTime`) of its [`Transaction`]. /// Returns true if this input enables the [`absolute::LockTime`] (aka `nLockTime`) of its
/// [`Transaction`].
/// ///
/// `nLockTime` is enabled if *any* input enables it. See [`Transaction::is_lock_time_enabled`] /// `nLockTime` is enabled if *any* input enables it. See [`Transaction::is_lock_time_enabled`]
/// to check the overall state. If none of the inputs enables it, the lock time value is simply /// to check the overall state. If none of the inputs enables it, the lock time value is simply
@ -250,28 +252,20 @@ impl Default for TxIn {
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))] #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Sequence(pub u32); pub struct Sequence(pub u32);
/// An error in creating relative lock-times.
#[derive(Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum RelativeLockTimeError {
/// The input was too large
IntegerOverflow(u32)
}
impl Sequence { impl Sequence {
/// The maximum allowable sequence number. /// The maximum allowable sequence number.
/// ///
/// This sequence number disables lock-time and replace-by-fee. /// This sequence number disables absolute lock time and replace-by-fee.
pub const MAX: Self = Sequence(0xFFFFFFFF); pub const MAX: Self = Sequence(0xFFFFFFFF);
/// Zero value sequence. /// Zero value sequence.
/// ///
/// This sequence number enables replace-by-fee and lock-time. /// This sequence number enables replace-by-fee and absolute lock time.
pub const ZERO: Self = Sequence(0); pub const ZERO: Self = Sequence(0);
/// The sequence number that enables absolute lock-time but disables replace-by-fee /// The sequence number that enables absolute lock time but disables replace-by-fee
/// and relative lock-time. /// and relative lock time.
pub const ENABLE_LOCKTIME_NO_RBF: Self = Sequence::MIN_NO_RBF; pub const ENABLE_LOCKTIME_NO_RBF: Self = Sequence::MIN_NO_RBF;
/// The sequence number that enables replace-by-fee and absolute lock-time but /// The sequence number that enables replace-by-fee and absolute lock time but
/// disables relative lock-time. /// disables relative lock time.
pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD); pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD);
/// The lowest sequence number that does not opt-in for replace-by-fee. /// The lowest sequence number that does not opt-in for replace-by-fee.
@ -282,9 +276,9 @@ impl Sequence {
/// ///
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]> /// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]>
const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE); const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE);
/// BIP-68 relative lock-time disable flag mask /// BIP-68 relative lock time disable flag mask.
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000; const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000;
/// BIP-68 relative lock-time type flag mask /// BIP-68 relative lock time type flag mask.
const LOCK_TYPE_MASK: u32 = 0x00400000; const LOCK_TYPE_MASK: u32 = 0x00400000;
/// Retuns `true` if the sequence number indicates that the transaction is finalised. /// Retuns `true` if the sequence number indicates that the transaction is finalised.
@ -342,11 +336,11 @@ impl Sequence {
/// ///
/// Will return an error if the input cannot be encoded in 16 bits. /// Will return an error if the input cannot be encoded in 16 bits.
#[inline] #[inline]
pub fn from_seconds_floor(seconds: u32) -> Result<Self, RelativeLockTimeError> { pub fn from_seconds_floor(seconds: u32) -> Result<Self, relative::Error> {
if let Ok(interval) = u16::try_from(seconds / 512) { if let Ok(interval) = u16::try_from(seconds / 512) {
Ok(Sequence::from_512_second_intervals(interval)) Ok(Sequence::from_512_second_intervals(interval))
} else { } else {
Err(RelativeLockTimeError::IntegerOverflow(seconds)) Err(relative::Error::IntegerOverflow(seconds))
} }
} }
@ -355,11 +349,11 @@ impl Sequence {
/// ///
/// Will return an error if the input cannot be encoded in 16 bits. /// Will return an error if the input cannot be encoded in 16 bits.
#[inline] #[inline]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, RelativeLockTimeError> { pub fn from_seconds_ceil(seconds: u32) -> Result<Self, relative::Error> {
if let Ok(interval) = u16::try_from((seconds + 511) / 512) { if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
Ok(Sequence::from_512_second_intervals(interval)) Ok(Sequence::from_512_second_intervals(interval))
} else { } else {
Err(RelativeLockTimeError::IntegerOverflow(seconds)) Err(relative::Error::IntegerOverflow(seconds))
} }
} }
@ -380,6 +374,31 @@ impl Sequence {
pub fn to_consensus_u32(self) -> u32 { pub fn to_consensus_u32(self) -> u32 {
self.0 self.0
} }
/// Creates a [`relative::LockTime`] from this [`Sequence`] number.
#[inline]
pub fn to_relative_lock_time(&self) -> Option<relative::LockTime> {
use crate::locktime::relative::{LockTime, Height, Time};
if !self.is_relative_lock_time() {
return None;
}
let lock_value = self.low_u16();
if self.is_time_locked() {
Some(LockTime::from(Time::from_512_second_intervals(lock_value)))
} else {
Some(LockTime::from(Height::from(lock_value)))
}
}
/// Returns the low 16 bits from sequence number.
///
/// BIP-68 only uses the low 16 bits for relative lock value.
fn low_u16(&self) -> u16 {
self.0 as u16
}
} }
impl Default for Sequence { impl Default for Sequence {
@ -413,26 +432,8 @@ impl fmt::UpperHex for Sequence {
} }
} }
impl fmt::Display for RelativeLockTimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::IntegerOverflow(val) => write!(f, "input of {} was too large", val)
}
}
}
impl_parse_str_through_int!(Sequence); impl_parse_str_through_int!(Sequence);
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for RelativeLockTimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::IntegerOverflow(_) => None
}
}
}
/// Bitcoin transaction output. /// Bitcoin transaction output.
/// ///
/// Defines new coins to be created as a result of the transaction, /// Defines new coins to be created as a result of the transaction,
@ -577,7 +578,7 @@ pub struct Transaction {
/// ///
/// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) /// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
/// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) /// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
pub lock_time: PackedLockTime, pub lock_time: absolute::PackedLockTime,
/// List of transaction inputs. /// List of transaction inputs.
pub input: Vec<TxIn>, pub input: Vec<TxIn>,
/// List of transaction outputs. /// List of transaction outputs.
@ -882,7 +883,7 @@ impl Transaction {
if !self.is_lock_time_enabled() { if !self.is_lock_time_enabled() {
return true; return true;
} }
LockTime::from(self.lock_time).is_satisfied_by(height, time) absolute::LockTime::from(self.lock_time).is_satisfied_by(height, time)
} }
/// Returns `true` if this transactions nLockTime is enabled ([BIP-65]). /// Returns `true` if this transactions nLockTime is enabled ([BIP-65]).
@ -1033,7 +1034,7 @@ mod tests {
use crate::blockdata::constants::WITNESS_SCALE_FACTOR; use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
use crate::consensus::encode::serialize; use crate::consensus::encode::serialize;
use crate::consensus::encode::deserialize; use crate::consensus::encode::deserialize;
@ -1132,7 +1133,7 @@ mod tests {
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()); "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
assert_eq!(realtx.input[0].previous_output.vout, 1); assert_eq!(realtx.input[0].previous_output.vout, 1);
assert_eq!(realtx.output.len(), 1); assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, PackedLockTime::ZERO); assert_eq!(realtx.lock_time, absolute::PackedLockTime::ZERO);
assert_eq!(format!("{:x}", realtx.txid()), assert_eq!(format!("{:x}", realtx.txid()),
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
@ -1166,7 +1167,7 @@ mod tests {
"7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string()); "7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string());
assert_eq!(realtx.input[0].previous_output.vout, 1); assert_eq!(realtx.input[0].previous_output.vout, 1);
assert_eq!(realtx.output.len(), 1); assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, PackedLockTime::ZERO); assert_eq!(realtx.lock_time, absolute::PackedLockTime::ZERO);
assert_eq!(format!("{:x}", realtx.txid()), assert_eq!(format!("{:x}", realtx.txid()),
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string()); "f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());

View File

@ -95,7 +95,7 @@ use core2::io;
pub use crate::address::{Address, AddressType}; pub use crate::address::{Address, AddressType};
pub use crate::blockdata::block::{self, Block, BlockHeader}; pub use crate::blockdata::block::{self, Block, BlockHeader};
pub use crate::blockdata::locktime::{self, LockTime, PackedLockTime}; pub use crate::blockdata::locktime::{self, absolute, relative};
pub use crate::blockdata::script::{self, Script}; pub use crate::blockdata::script::{self, Script};
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::blockdata::transaction::SigHashType; pub use crate::blockdata::transaction::SigHashType;

View File

@ -78,10 +78,10 @@ impl<R: DerefMut<Target = Transaction>> SigHashCache<R> {
/// panics if `input_index` is out of bounds with respect of the number of inputs /// panics if `input_index` is out of bounds with respect of the number of inputs
/// ///
/// ``` /// ```
/// use bitcoin::{EcdsaSighashType, Script, Transaction, PackedLockTime};
/// use bitcoin::util::bip143::SigHashCache; /// use bitcoin::util::bip143::SigHashCache;
/// use bitcoin::{absolute, EcdsaSighashType, Script, Transaction};
/// ///
/// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() }; /// let mut tx_to_sign = Transaction { version: 2, lock_time: absolute::PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
/// let input_count = tx_to_sign.input.len(); /// let input_count = tx_to_sign.input.len();
/// ///
/// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign); /// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign);

View File

@ -372,17 +372,18 @@ impl BlockTransactions {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::blockdata::locktime::absolute;
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
use crate::hashes::hex::FromHex; use crate::hashes::hex::FromHex;
use crate::{ use crate::{
Block, BlockHash, BlockHeader, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode, Block, BlockHash, BlockHeader, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode,
TxOut, Txid, Witness, LockTime, TxOut, Txid, Witness,
}; };
fn dummy_tx(nonce: &[u8]) -> Transaction { fn dummy_tx(nonce: &[u8]) -> Transaction {
Transaction { Transaction {
version: 1, version: 1,
lock_time: LockTime::from_consensus(2).into(), lock_time: absolute::LockTime::from_consensus(2).into(),
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::new(Txid::hash(nonce), 0), previous_output: OutPoint::new(Txid::hash(nonce), 0),
script_sig: Script::new(), script_sig: Script::new(),

View File

@ -335,7 +335,7 @@ impl Decodable for PartiallySignedTransaction {
mod tests { mod tests {
use super::*; use super::*;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
use crate::hashes::hex::FromHex; use crate::hashes::hex::FromHex;
use crate::hashes::{sha256, hash160, Hash, ripemd160}; use crate::hashes::{sha256, hash160, Hash, ripemd160};
use crate::hash_types::Txid; use crate::hash_types::Txid;
@ -359,7 +359,7 @@ mod tests {
let psbt = PartiallySignedTransaction { let psbt = PartiallySignedTransaction {
unsigned_tx: Transaction { unsigned_tx: Transaction {
version: 2, version: 2,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![], input: vec![],
output: vec![], output: vec![],
}, },
@ -429,7 +429,7 @@ mod tests {
let expected = PartiallySignedTransaction { let expected = PartiallySignedTransaction {
unsigned_tx: Transaction { unsigned_tx: Transaction {
version: 2, version: 2,
lock_time: PackedLockTime(1257139), lock_time: absolute::PackedLockTime(1257139),
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex( txid: Txid::from_hex(
@ -502,7 +502,7 @@ mod tests {
// create some values to use in the PSBT // create some values to use in the PSBT
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex("e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389").unwrap(), txid: Txid::from_hex("e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389").unwrap(),
@ -600,7 +600,7 @@ mod tests {
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence}; use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
use crate::consensus::encode::serialize_hex; use crate::consensus::encode::serialize_hex;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
use crate::util::psbt::map::{Map, Input, Output}; use crate::util::psbt::map::{Map, Input, Output};
use crate::util::psbt::raw; use crate::util::psbt::raw;
use crate::util::psbt::{PartiallySignedTransaction, Error}; use crate::util::psbt::{PartiallySignedTransaction, Error};
@ -690,7 +690,7 @@ mod tests {
let unserialized = PartiallySignedTransaction { let unserialized = PartiallySignedTransaction {
unsigned_tx: Transaction { unsigned_tx: Transaction {
version: 2, version: 2,
lock_time: PackedLockTime(1257139), lock_time: absolute::PackedLockTime(1257139),
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex( txid: Txid::from_hex(
@ -721,7 +721,7 @@ mod tests {
inputs: vec![Input { inputs: vec![Input {
non_witness_utxo: Some(Transaction { non_witness_utxo: Some(Transaction {
version: 1, version: 1,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex( txid: Txid::from_hex(
@ -1002,7 +1002,7 @@ mod tests {
let mut unserialized = PartiallySignedTransaction { let mut unserialized = PartiallySignedTransaction {
unsigned_tx: Transaction { unsigned_tx: Transaction {
version: 2, version: 2,
lock_time: PackedLockTime(1257139), lock_time: absolute::PackedLockTime(1257139),
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex( txid: Txid::from_hex(
@ -1033,7 +1033,7 @@ mod tests {
inputs: vec![Input { inputs: vec![Input {
non_witness_utxo: Some(Transaction { non_witness_utxo: Some(Transaction {
version: 1, version: 1,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: Txid::from_hex( txid: Txid::from_hex(

View File

@ -1035,9 +1035,9 @@ impl<R: DerefMut<Target=Transaction>> SighashCache<R> {
/// ///
/// This allows in-line signing such as /// This allows in-line signing such as
/// ``` /// ```
/// use bitcoin::{EcdsaSighashType, PackedLockTime, Script, SighashCache, Transaction}; /// use bitcoin::{absolute, EcdsaSighashType, SighashCache, Transaction, Script};
/// ///
/// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() }; /// let mut tx_to_sign = Transaction { version: 2, lock_time: absolute::PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
/// let input_count = tx_to_sign.input.len(); /// let input_count = tx_to_sign.input.len();
/// ///
/// let mut sig_hasher = SighashCache::new(&mut tx_to_sign); /// let mut sig_hasher = SighashCache::new(&mut tx_to_sign);
@ -1099,7 +1099,7 @@ mod tests {
use secp256k1::{self, SecretKey, XOnlyPublicKey}; use secp256k1::{self, SecretKey, XOnlyPublicKey};
use crate::{Script, Transaction, TxIn, TxOut, EcdsaSighashType, Address}; use crate::{Script, Transaction, TxIn, TxOut, EcdsaSighashType, Address};
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::absolute;
use crate::consensus::deserialize; use crate::consensus::deserialize;
use crate::hashes::hex::{FromHex, ToHex}; use crate::hashes::hex::{FromHex, ToHex};
use crate::hashes::{Hash, HashEngine}; use crate::hashes::{Hash, HashEngine};
@ -1119,7 +1119,7 @@ mod tests {
// We need a tx with more inputs than outputs. // We need a tx with more inputs than outputs.
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn::default(), TxIn::default()], input: vec![TxIn::default(), TxIn::default()],
output: vec![TxOut::default()], output: vec![TxOut::default()],
}; };
@ -1300,7 +1300,7 @@ mod tests {
fn test_sighash_errors() { fn test_sighash_errors() {
let dumb_tx = Transaction { let dumb_tx = Transaction {
version: 0, version: 0,
lock_time: PackedLockTime::ZERO, lock_time: absolute::PackedLockTime::ZERO,
input: vec![TxIn::default()], input: vec![TxIn::default()],
output: vec![], output: vec![],
}; };