From 9f7d934f5d710cc25aa90158aab545760237553b Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Aug 2022 11:24:56 +1000 Subject: [PATCH 1/3] Move the locktime module to absolute In preparation for adding a relative lock time type move the `locktime` module to a new module called `absolute`. Use qualified path for locktime types (e.g. `absolute::PackedLockTime`) to improve readability. --- examples/ecdsa-psbt.rs | 5 ++-- src/blockdata/constants.rs | 8 +++--- .../{locktime.rs => locktime/absolute.rs} | 27 ++++++++++--------- src/blockdata/locktime/mod.rs | 7 +++++ src/blockdata/transaction.rs | 15 ++++++----- src/lib.rs | 2 +- src/util/bip143.rs | 4 +-- src/util/bip152.rs | 5 ++-- src/util/psbt/mod.rs | 18 ++++++------- src/util/sighash.rs | 10 +++---- 10 files changed, 56 insertions(+), 45 deletions(-) rename src/blockdata/{locktime.rs => locktime/absolute.rs} (97%) create mode 100644 src/blockdata/locktime/mod.rs diff --git a/examples/ecdsa-psbt.rs b/examples/ecdsa-psbt.rs index 70b1399e..5c595e11 100644 --- a/examples/ecdsa-psbt.rs +++ b/examples/ecdsa-psbt.rs @@ -34,6 +34,7 @@ use std::str::FromStr; use bitcoin::consensus::encode; use bitcoin::hashes::hex::{self, FromHex}; +use bitcoin::locktime::absolute; use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::util::amount::ParseAmountError; use bitcoin::util::bip32::{ @@ -42,7 +43,7 @@ use bitcoin::util::bip32::{ }; use bitcoin::util::psbt::{self, Input, Psbt, PsbtSighashType}; 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, }; @@ -206,7 +207,7 @@ impl WatchOnly { let tx = Transaction { version: 2, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex(INPUT_UTXO_TXID)?, diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index ba01eb06..27de8845 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -16,7 +16,7 @@ use crate::hashes::hex::{self, HexIterator}; use crate::hashes::{Hash, sha256d}; use crate::blockdata::opcodes; 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::block::{Block, BlockHeader}; use crate::blockdata::witness::Witness; @@ -72,7 +72,7 @@ fn bitcoin_genesis_tx() -> Transaction { // Base let mut ret = Transaction { version: 1, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![], output: vec![], }; @@ -200,7 +200,7 @@ mod test { use crate::hashes::hex::{ToHex, FromHex}; use crate::network::constants::Network; use crate::consensus::encode::serialize; - use crate::blockdata::locktime::PackedLockTime; + use crate::blockdata::locktime::absolute; #[test] fn bitcoin_genesis_first_transaction() { @@ -218,7 +218,7 @@ mod test { assert_eq!(serialize(&gen.output[0].script_pubkey), Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap()); 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"); } diff --git a/src/blockdata/locktime.rs b/src/blockdata/locktime/absolute.rs similarity index 97% rename from src/blockdata/locktime.rs rename to src/blockdata/locktime/absolute.rs index 3c03548d..f2fedb03 100644 --- a/src/blockdata/locktime.rs +++ b/src/blockdata/locktime/absolute.rs @@ -47,7 +47,8 @@ pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000; /// /// # Examples /// ``` -/// # use bitcoin::{Amount, PackedLockTime, LockTime}; +/// # use bitcoin::Amount; +/// # use bitcoin::absolute::{PackedLockTime, LockTime}; /// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] /// struct S { /// lock_time: PackedLockTime, @@ -152,7 +153,7 @@ impl fmt::UpperHex for PackedLockTime { /// /// # Examples /// ``` -/// # use bitcoin::{LockTime, LockTime::*}; +/// # use bitcoin::absolute::{LockTime, LockTime::*}; /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY /// # let lock_time = LockTime::from_consensus(100); // nLockTime /// // To compare lock times there are various `is_satisfied_*` methods, you may also use: @@ -171,7 +172,7 @@ pub enum LockTime { /// /// # Examples /// ```rust - /// use bitcoin::LockTime; + /// use bitcoin::absolute::LockTime; /// /// let block: u32 = 741521; /// let n = LockTime::from_height(block).expect("valid height"); @@ -183,7 +184,7 @@ pub enum LockTime { /// /// # Examples /// ```rust - /// use bitcoin::LockTime; + /// use bitcoin::absolute::LockTime; /// /// let seconds: u32 = 1653195600; // May 22nd, 5am UTC. /// let n = LockTime::from_time(seconds).expect("valid time"); @@ -203,7 +204,7 @@ impl LockTime { /// # Examples /// /// ```rust - /// # use bitcoin::LockTime; + /// # use bitcoin::absolute::LockTime; /// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY /// /// // `from_consensus` roundtrips as expected with `to_consensus_u32`. @@ -225,7 +226,7 @@ impl LockTime { /// /// # Examples /// ```rust - /// # use bitcoin::LockTime; + /// # use bitcoin::absolute::LockTime; /// assert!(LockTime::from_height(741521).is_ok()); /// assert!(LockTime::from_height(1653195600).is_err()); /// ``` @@ -241,7 +242,7 @@ impl LockTime { /// /// # Examples /// ```rust - /// # use bitcoin::LockTime; + /// # use bitcoin::absolute::LockTime; /// assert!(LockTime::from_time(1653195600).is_ok()); /// assert!(LockTime::from_time(741521).is_err()); /// ``` @@ -283,7 +284,7 @@ impl LockTime { /// /// # Examples /// ```no_run - /// # use bitcoin::locktime::{LockTime, Height, Time}; + /// # use bitcoin::absolute::{LockTime, Height, Time}; /// // Can be implemented if block chain data is available. /// fn get_height() -> Height { todo!("return the current block height") } /// fn get_time() -> Time { todo!("return the current block time") } @@ -314,7 +315,7 @@ impl LockTime { /// # Examples /// /// ```rust - /// # use bitcoin::{LockTime, LockTime::*}; + /// # use bitcoin::absolute::{LockTime, LockTime::*}; /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY /// # let lock_time = LockTime::from_consensus(100); // nLockTime /// @@ -410,7 +411,7 @@ impl Height { /// /// # Examples /// ```rust - /// use bitcoin::locktime::Height; + /// use bitcoin::locktime::absolute::Height; /// /// let h: u32 = 741521; /// let height = Height::from_consensus(h).expect("invalid height value"); @@ -429,7 +430,7 @@ impl Height { /// /// # Examples /// ```rust - /// use bitcoin::LockTime; + /// use bitcoin::absolute::LockTime; /// /// let n_lock_time: u32 = 741521; /// let lock_time = LockTime::from_consensus(n_lock_time); @@ -493,7 +494,7 @@ impl Time { /// /// # Examples /// ```rust - /// use bitcoin::locktime::Time; + /// use bitcoin::locktime::absolute::Time; /// /// let t: u32 = 1653195600; // May 22nd, 5am UTC. /// let time = Time::from_consensus(t).expect("invalid time value"); @@ -512,7 +513,7 @@ impl Time { /// /// # Examples /// ```rust - /// use bitcoin::LockTime; + /// use bitcoin::absolute::LockTime; /// /// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC. /// let lock_time = LockTime::from_consensus(n_lock_time); diff --git a/src/blockdata/locktime/mod.rs b/src/blockdata/locktime/mod.rs new file mode 100644 index 00000000..c4635e68 --- /dev/null +++ b/src/blockdata/locktime/mod.rs @@ -0,0 +1,7 @@ +// Rust Bitcoin Library - Written by the rust-bitcoin developers. +// SPDX-License-Identifier: CC0-1.0 + +//! Provides absolute locktime. +//! + +pub mod absolute; diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 442eb5dd..736a4234 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -25,7 +25,7 @@ use crate::blockdata::constants::WITNESS_SCALE_FACTOR; #[cfg(feature="bitcoinconsensus")] use crate::blockdata::script; use crate::blockdata::script::Script; use crate::blockdata::witness::Witness; -use crate::blockdata::locktime::{LockTime, PackedLockTime, Height, Time}; +use crate::blockdata::locktime::absolute::{self, Height, Time}; use crate::consensus::{encode, Decodable, Encodable}; use crate::hash_types::{Sighash, Txid, Wtxid}; use crate::VarInt; @@ -207,7 +207,8 @@ pub struct 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`] /// to check the overall state. If none of the inputs enables it, the lock time value is simply @@ -577,7 +578,7 @@ pub struct Transaction { /// /// * [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) - pub lock_time: PackedLockTime, + pub lock_time: absolute::PackedLockTime, /// List of transaction inputs. pub input: Vec, /// List of transaction outputs. @@ -882,7 +883,7 @@ impl Transaction { if !self.is_lock_time_enabled() { 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]). @@ -1033,7 +1034,7 @@ mod tests { use crate::blockdata::constants::WITNESS_SCALE_FACTOR; use crate::blockdata::script::Script; - use crate::blockdata::locktime::PackedLockTime; + use crate::blockdata::locktime::absolute; use crate::consensus::encode::serialize; use crate::consensus::encode::deserialize; @@ -1132,7 +1133,7 @@ mod tests { "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()); assert_eq!(realtx.input[0].previous_output.vout, 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()), "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); @@ -1166,7 +1167,7 @@ mod tests { "7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string()); assert_eq!(realtx.input[0].previous_output.vout, 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()), "f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string()); diff --git a/src/lib.rs b/src/lib.rs index 6ab36cf0..2129acca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ use core2::io; pub use crate::address::{Address, AddressType}; pub use crate::blockdata::block::{self, Block, BlockHeader}; -pub use crate::blockdata::locktime::{self, LockTime, PackedLockTime}; +pub use crate::blockdata::locktime::{self, absolute}; pub use crate::blockdata::script::{self, Script}; #[allow(deprecated)] pub use crate::blockdata::transaction::SigHashType; diff --git a/src/util/bip143.rs b/src/util/bip143.rs index 4386b7a1..3ac2f7bd 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -78,10 +78,10 @@ impl> SigHashCache { /// 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::{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 mut sig_hasher = SigHashCache::new(&mut tx_to_sign); diff --git a/src/util/bip152.rs b/src/util/bip152.rs index 1638a1e6..5ffc9c59 100644 --- a/src/util/bip152.rs +++ b/src/util/bip152.rs @@ -372,17 +372,18 @@ impl BlockTransactions { #[cfg(test)] mod test { use super::*; + use crate::blockdata::locktime::absolute; use crate::consensus::encode::{deserialize, serialize}; use crate::hashes::hex::FromHex; use crate::{ Block, BlockHash, BlockHeader, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode, - TxOut, Txid, Witness, LockTime, + TxOut, Txid, Witness, }; fn dummy_tx(nonce: &[u8]) -> Transaction { Transaction { version: 1, - lock_time: LockTime::from_consensus(2).into(), + lock_time: absolute::LockTime::from_consensus(2).into(), input: vec![TxIn { previous_output: OutPoint::new(Txid::hash(nonce), 0), script_sig: Script::new(), diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 6ee66e82..6e88bd39 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -335,7 +335,7 @@ impl Decodable for PartiallySignedTransaction { mod tests { use super::*; - use crate::blockdata::locktime::PackedLockTime; + use crate::blockdata::locktime::absolute; use crate::hashes::hex::FromHex; use crate::hashes::{sha256, hash160, Hash, ripemd160}; use crate::hash_types::Txid; @@ -359,7 +359,7 @@ mod tests { let psbt = PartiallySignedTransaction { unsigned_tx: Transaction { version: 2, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![], output: vec![], }, @@ -429,7 +429,7 @@ mod tests { let expected = PartiallySignedTransaction { unsigned_tx: Transaction { version: 2, - lock_time: PackedLockTime(1257139), + lock_time: absolute::PackedLockTime(1257139), input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex( @@ -502,7 +502,7 @@ mod tests { // create some values to use in the PSBT let tx = Transaction { version: 1, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex("e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389").unwrap(), @@ -600,7 +600,7 @@ mod tests { use crate::blockdata::script::Script; use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence}; 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::raw; use crate::util::psbt::{PartiallySignedTransaction, Error}; @@ -690,7 +690,7 @@ mod tests { let unserialized = PartiallySignedTransaction { unsigned_tx: Transaction { version: 2, - lock_time: PackedLockTime(1257139), + lock_time: absolute::PackedLockTime(1257139), input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex( @@ -721,7 +721,7 @@ mod tests { inputs: vec![Input { non_witness_utxo: Some(Transaction { version: 1, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex( @@ -1002,7 +1002,7 @@ mod tests { let mut unserialized = PartiallySignedTransaction { unsigned_tx: Transaction { version: 2, - lock_time: PackedLockTime(1257139), + lock_time: absolute::PackedLockTime(1257139), input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex( @@ -1033,7 +1033,7 @@ mod tests { inputs: vec![Input { non_witness_utxo: Some(Transaction { version: 1, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { txid: Txid::from_hex( diff --git a/src/util/sighash.rs b/src/util/sighash.rs index b3bdb592..8c7ae96b 100644 --- a/src/util/sighash.rs +++ b/src/util/sighash.rs @@ -1035,9 +1035,9 @@ impl> SighashCache { /// /// 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 mut sig_hasher = SighashCache::new(&mut tx_to_sign); @@ -1099,7 +1099,7 @@ mod tests { use secp256k1::{self, SecretKey, XOnlyPublicKey}; use crate::{Script, Transaction, TxIn, TxOut, EcdsaSighashType, Address}; - use crate::blockdata::locktime::PackedLockTime; + use crate::blockdata::locktime::absolute; use crate::consensus::deserialize; use crate::hashes::hex::{FromHex, ToHex}; use crate::hashes::{Hash, HashEngine}; @@ -1119,7 +1119,7 @@ mod tests { // We need a tx with more inputs than outputs. let tx = Transaction { version: 1, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn::default(), TxIn::default()], output: vec![TxOut::default()], }; @@ -1300,7 +1300,7 @@ mod tests { fn test_sighash_errors() { let dumb_tx = Transaction { version: 0, - lock_time: PackedLockTime::ZERO, + lock_time: absolute::PackedLockTime::ZERO, input: vec![TxIn::default()], output: vec![], }; From cbfa8cff4c140ccebeb2ce1cac7992c2731ac40a Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Aug 2022 11:33:54 +1000 Subject: [PATCH 2/3] Improve lock time docs Bitcoin lock times are easy to get confused, add absolute/relative in various places to help clarify things. --- src/blockdata/locktime/absolute.rs | 5 +++-- src/blockdata/transaction.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/blockdata/locktime/absolute.rs b/src/blockdata/locktime/absolute.rs index f2fedb03..aaece729 100644 --- a/src/blockdata/locktime/absolute.rs +++ b/src/blockdata/locktime/absolute.rs @@ -141,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`] /// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`. @@ -156,7 +157,7 @@ impl fmt::UpperHex for PackedLockTime { /// # use bitcoin::absolute::{LockTime, LockTime::*}; /// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY /// # 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) { /// (Blocks(n), Blocks(lock_time)) => n <= lock_time, /// (Seconds(n), Seconds(lock_time)) => n <= lock_time, diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 736a4234..f0fb57a5 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -262,17 +262,17 @@ pub enum RelativeLockTimeError { impl Sequence { /// 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); /// 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); - /// The sequence number that enables absolute lock-time but disables replace-by-fee - /// and relative lock-time. + /// The sequence number that enables absolute lock time but disables replace-by-fee + /// and relative lock time. pub const ENABLE_LOCKTIME_NO_RBF: Self = Sequence::MIN_NO_RBF; - /// The sequence number that enables replace-by-fee and absolute lock-time but - /// disables relative lock-time. + /// The sequence number that enables replace-by-fee and absolute lock time but + /// disables relative lock time. pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD); /// The lowest sequence number that does not opt-in for replace-by-fee. @@ -283,9 +283,9 @@ impl Sequence { /// /// [BIP-125]: 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; - /// BIP-68 relative lock-time type flag mask + /// BIP-68 relative lock time type flag mask. const LOCK_TYPE_MASK: u32 = 0x00400000; /// Retuns `true` if the sequence number indicates that the transaction is finalised. From da95c3a489c18399d6c3170a0c8af0b5fa8d7cf9 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 16 Aug 2022 13:14:24 +1000 Subject: [PATCH 3/3] Add newtype `relative::LockTime` We recently added the `Sequence` type and we explicitly did not include relative lock time logic. Add a new module `relative` and new type `LockTime` to represent relative lock times as defined by BIP 68. --- src/blockdata/locktime/mod.rs | 3 +- src/blockdata/locktime/relative.rs | 289 +++++++++++++++++++++++++++++ src/blockdata/transaction.rs | 60 +++--- src/lib.rs | 2 +- 4 files changed, 322 insertions(+), 32 deletions(-) create mode 100644 src/blockdata/locktime/relative.rs diff --git a/src/blockdata/locktime/mod.rs b/src/blockdata/locktime/mod.rs index c4635e68..5100d702 100644 --- a/src/blockdata/locktime/mod.rs +++ b/src/blockdata/locktime/mod.rs @@ -1,7 +1,8 @@ // Rust Bitcoin Library - Written by the rust-bitcoin developers. // SPDX-License-Identifier: CC0-1.0 -//! Provides absolute locktime. +//! Provides absolute and relative locktimes. //! pub mod absolute; +pub mod relative; diff --git a/src/blockdata/locktime/relative.rs b/src/blockdata/locktime/relative.rs new file mode 100644 index 00000000..7502c969 --- /dev/null +++ b/src/blockdata/locktime/relative.rs @@ -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 { + 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 { + use LockTime::*; + + match *self { + Time(ref time) => Ok(time.value() <= t.value()), + Blocks(ref height) => Err(Error::IncompatibleHeight(*self, *height)), + } + } +} + +impl From for LockTime { + #[inline] + fn from(h: Height) -> Self { + LockTime::Blocks(h) + } +} + +impl From