Add lock time types
Add a `LockTime` type to hold the nLockTime `u32` value. Use it in `Transaction` for `lock_time` instead of a `u32`. Make it public so this new type can be used by rust-miniscript and other downstream projects. Add a `PackedLockTime` type that wraps a raw `u32` and derives `Ord`, this type is for wrapping a consensus lock time value for nesting in types that would like to derive `Ord`.
This commit is contained in:
parent
1390ee12ec
commit
0ed78e543b
|
@ -43,8 +43,8 @@ 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, Amount, Network, OutPoint, PrivateKey, PublicKey, Script, Sequence, Transaction, TxIn,
|
Address, Amount, Network, OutPoint, PackedLockTime, PrivateKey, PublicKey, Script, Sequence,
|
||||||
TxOut, Txid, Witness,
|
Transaction, TxIn, TxOut, Txid, Witness,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::psbt_sign::*;
|
use self::psbt_sign::*;
|
||||||
|
@ -207,7 +207,7 @@ impl WatchOnly {
|
||||||
|
|
||||||
let tx = Transaction {
|
let tx = Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 0,
|
lock_time: 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)?,
|
||||||
|
|
|
@ -16,6 +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::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;
|
||||||
|
@ -73,7 +74,7 @@ fn bitcoin_genesis_tx() -> Transaction {
|
||||||
// Base
|
// Base
|
||||||
let mut ret = Transaction {
|
let mut ret = Transaction {
|
||||||
version: 1,
|
version: 1,
|
||||||
lock_time: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
};
|
};
|
||||||
|
@ -201,6 +202,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;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bitcoin_genesis_first_transaction() {
|
fn bitcoin_genesis_first_transaction() {
|
||||||
|
@ -218,7 +220,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, 0);
|
assert_eq!(gen.lock_time, PackedLockTime::ZERO);
|
||||||
|
|
||||||
assert_eq!(gen.wtxid().to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
assert_eq!(gen.wtxid().to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,762 @@
|
||||||
|
// Rust Bitcoin Library
|
||||||
|
// Written in 2022 by
|
||||||
|
// Tobin C. Harding <me@tobin.cc>
|
||||||
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
|
// copyright and related and neighboring rights to this software to
|
||||||
|
// the public domain worldwide. This software is distributed without
|
||||||
|
// any warranty.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
// along with this software.
|
||||||
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! Provides type [`LockTime`] that implements the logic around nLockTime/OP_CHECKLOCKTIMEVERIFY.
|
||||||
|
//!
|
||||||
|
//! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
|
||||||
|
//! whether `LockTime < LOCKTIME_THRESHOLD`.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use core::{mem, fmt};
|
||||||
|
use core::cmp::{PartialOrd, Ordering};
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
use core::str::FromStr;
|
||||||
|
use core::num::ParseIntError;
|
||||||
|
|
||||||
|
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||||
|
use crate::io::{self, Read, Write};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::internal_macros::write_err;
|
||||||
|
|
||||||
|
/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
|
||||||
|
///
|
||||||
|
/// `LockTime` values _below_ the threshold are interpreted as block heights, values _above_ (or
|
||||||
|
/// equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch).
|
||||||
|
///
|
||||||
|
/// Bitcoin is able to safely use this value because a block height greater than 500,000,000 would
|
||||||
|
/// never occur because it would represent a height in approximately 9500 years. Conversely, block
|
||||||
|
/// times under 500,000,000 will never happen because they would represent times before 1986 which
|
||||||
|
/// are, for obvious reasons, not useful within the Bitcoin network.
|
||||||
|
///
|
||||||
|
/// [Bitcoin Core]: https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
|
||||||
|
pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
|
||||||
|
|
||||||
|
/// Packed lock time wraps a [`LockTime`] consensus value i.e., the raw `u32` used by the network.
|
||||||
|
///
|
||||||
|
/// This struct may be preferred in performance-critical applications because it's slightly smaller
|
||||||
|
/// than [`LockTime`] and has a bit more performant (de)serialization. In particular, this may be
|
||||||
|
/// relevant when the value is not processed, just passed around. Note however that the difference
|
||||||
|
/// is super-small, so unless you do something extreme you shouldn't worry about it.
|
||||||
|
///
|
||||||
|
/// This type implements a naive ordering based on the `u32`, this is _not_ a semantically correct
|
||||||
|
/// ordering for a lock time, hence [`LockTime`] does not implement `Ord`. This type is useful if
|
||||||
|
/// you want to use a lock time as part of a struct and wish to derive `Ord`. For all other uses,
|
||||||
|
/// consider using [`LockTime`] directly.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use bitcoin::{Amount, PackedLockTime, LockTime};
|
||||||
|
/// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
|
/// struct S {
|
||||||
|
/// lock_time: PackedLockTime,
|
||||||
|
/// amount: Amount,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let _ = S {
|
||||||
|
/// lock_time: LockTime::from_consensus(741521).into(),
|
||||||
|
/// amount: Amount::from_sat(10_000_000),
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||||
|
pub struct PackedLockTime(pub u32);
|
||||||
|
|
||||||
|
impl PackedLockTime {
|
||||||
|
/// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a
|
||||||
|
/// transaction with nLocktime==0 is able to be included immediately in any block.
|
||||||
|
pub const ZERO: PackedLockTime = PackedLockTime(0);
|
||||||
|
|
||||||
|
/// Returns the inner `u32`.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_u32(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PackedLockTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for PackedLockTime {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.0.consensus_encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for PackedLockTime {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||||
|
u32::consensus_decode(r).map(PackedLockTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LockTime> for PackedLockTime {
|
||||||
|
fn from(n: LockTime) -> Self {
|
||||||
|
PackedLockTime(n.to_consensus_u32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PackedLockTime> for LockTime {
|
||||||
|
fn from(n: PackedLockTime) -> Self {
|
||||||
|
LockTime::from_consensus(n.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&LockTime> for PackedLockTime {
|
||||||
|
fn from(n: &LockTime) -> Self {
|
||||||
|
PackedLockTime(n.to_consensus_u32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PackedLockTime> for LockTime {
|
||||||
|
fn from(n: &PackedLockTime) -> Self {
|
||||||
|
LockTime::from_consensus(n.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PackedLockTime> for u32 {
|
||||||
|
fn from(p: PackedLockTime) -> Self {
|
||||||
|
p.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PackedLockTime {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.parse().map(PackedLockTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for PackedLockTime {
|
||||||
|
type Error = ParseIntError;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
PackedLockTime::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for PackedLockTime {
|
||||||
|
type Error = ParseIntError;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
PackedLockTime::from_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for PackedLockTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::UpperHex for PackedLockTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:X}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A 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`.
|
||||||
|
///
|
||||||
|
/// ### Relevant BIPs
|
||||||
|
///
|
||||||
|
/// * [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)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use bitcoin::{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:
|
||||||
|
/// let is_satisfied = match (n, lock_time) {
|
||||||
|
/// (Blocks(n), Blocks(lock_time)) => n <= lock_time,
|
||||||
|
/// (Seconds(n), Seconds(lock_time)) => n <= lock_time,
|
||||||
|
/// _ => panic!("handle invalid comparison error"),
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
#[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.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::LockTime;
|
||||||
|
///
|
||||||
|
/// let block: u32 = 741521;
|
||||||
|
/// let n = LockTime::from_height(block).expect("valid height");
|
||||||
|
/// assert!(n.is_block_height());
|
||||||
|
/// assert_eq!(n.to_consensus_u32(), block);
|
||||||
|
/// ```
|
||||||
|
Blocks(Height),
|
||||||
|
/// A UNIX timestamp lock time value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::LockTime;
|
||||||
|
///
|
||||||
|
/// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
/// let n = LockTime::from_time(seconds).expect("valid time");
|
||||||
|
/// assert!(n.is_block_time());
|
||||||
|
/// assert_eq!(n.to_consensus_u32(), seconds);
|
||||||
|
/// ```
|
||||||
|
Seconds(Time),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockTime {
|
||||||
|
/// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a
|
||||||
|
/// transaction with nLocktime==0 is able to be included immediately in any block.
|
||||||
|
pub const ZERO: LockTime = LockTime::Blocks(Height(0));
|
||||||
|
|
||||||
|
/// Constructs a `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bitcoin::LockTime;
|
||||||
|
/// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
|
||||||
|
///
|
||||||
|
/// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
|
||||||
|
/// let n_lock_time: u32 = 741521;
|
||||||
|
/// let lock_time = LockTime::from_consensus(n_lock_time);
|
||||||
|
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
|
||||||
|
#[inline]
|
||||||
|
pub fn from_consensus(n: u32) -> Self {
|
||||||
|
if is_block_height(n) {
|
||||||
|
Self::Blocks(Height::from_consensus(n).expect("n is valid"))
|
||||||
|
} else {
|
||||||
|
Self::Seconds(Time::from_consensus(n).expect("n is valid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a `LockTime` from `n`, expecting `n` to be a valid block height.
|
||||||
|
///
|
||||||
|
/// See [`LOCK_TIME_THRESHOLD`] for definition of a valid height value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use bitcoin::LockTime;
|
||||||
|
/// assert!(LockTime::from_height(741521).is_ok());
|
||||||
|
/// assert!(LockTime::from_height(1653195600).is_err());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_height(n: u32) -> Result<Self, Error> {
|
||||||
|
let height = Height::from_consensus(n)?;
|
||||||
|
Ok(LockTime::Blocks(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a `LockTime` from `n`, expecting `n` to be a valid block time.
|
||||||
|
///
|
||||||
|
/// See [`LOCK_TIME_THRESHOLD`] for definition of a valid time value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use bitcoin::LockTime;
|
||||||
|
/// assert!(LockTime::from_time(1653195600).is_ok());
|
||||||
|
/// assert!(LockTime::from_time(741521).is_err());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_time(n: u32) -> Result<Self, Error> {
|
||||||
|
let time = Time::from_consensus(n)?;
|
||||||
|
Ok(LockTime::Seconds(time))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if both lock times use the same unit i.e., both height based or both time based.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_same_unit(&self, other: LockTime) -> bool {
|
||||||
|
mem::discriminant(self) == mem::discriminant(&other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this lock time value is a block height.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_block_height(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
LockTime::Blocks(_) => true,
|
||||||
|
LockTime::Seconds(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this lock time value is a block time (UNIX timestamp).
|
||||||
|
#[inline]
|
||||||
|
pub fn is_block_time(&self) -> bool {
|
||||||
|
!self.is_block_height()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this timelock constraint is satisfied by the respective `height`/`time`.
|
||||||
|
///
|
||||||
|
/// If `self` is a blockheight based lock then it is checked against `height` and if `self` is a
|
||||||
|
/// blocktime based lock it is checked against `time`.
|
||||||
|
///
|
||||||
|
/// A 'timelock constraint' refers to the `n` from `n OP_CHEKCLOCKTIMEVERIFY`, this constraint
|
||||||
|
/// is satisfied if a transaction with nLockTime ([`crate::Transaction::lock_time`]) set to
|
||||||
|
/// `height`/`time` is valid.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```no_run
|
||||||
|
/// # use bitcoin::blockdata::locktime::{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") }
|
||||||
|
///
|
||||||
|
/// let n = LockTime::from_consensus(741521); // `n OP_CHEKCLOCKTIMEVERIFY`.
|
||||||
|
/// if n.is_satisfied_by(get_height(), get_time()) {
|
||||||
|
/// // Can create and mine a transaction that satisfies the OP_CLTV timelock constraint.
|
||||||
|
/// }
|
||||||
|
/// ````
|
||||||
|
#[inline]
|
||||||
|
pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
|
||||||
|
use LockTime::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Blocks(n) => n <= height,
|
||||||
|
Seconds(n) => n <= time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the inner `u32` value. This is the value used when creating this `LockTime`
|
||||||
|
/// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// Do not compare values return by this method. The whole point of the `LockTime` type is to
|
||||||
|
/// assist in doing correct comparisons. Either use `is_satisfied_by` or use the pattern below:
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bitcoin::{LockTime, LockTime::*};
|
||||||
|
/// # let n = LockTime::from_consensus(100); // n OP_CHECKLOCKTIMEVERIFY
|
||||||
|
/// # let lock_time = LockTime::from_consensus(100); // nLockTime
|
||||||
|
///
|
||||||
|
/// let is_satisfied = match (n, lock_time) {
|
||||||
|
/// (Blocks(n), Blocks(lock_time)) => n <= lock_time,
|
||||||
|
/// (Seconds(n), Seconds(lock_time)) => n <= lock_time,
|
||||||
|
/// _ => panic!("invalid comparison"),
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // Or, if you have Rust 1.53 or greater
|
||||||
|
/// // let is_satisfied = n.partial_cmp(&lock_time).expect("invalid comparison").is_le();
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_consensus_u32(&self) -> u32 {
|
||||||
|
match *self {
|
||||||
|
LockTime::Blocks(ref h) => h.to_consensus_u32(),
|
||||||
|
LockTime::Seconds(ref t) => t.to_consensus_u32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for LockTime {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.parse().map(LockTime::from_consensus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for LockTime {
|
||||||
|
type Error = ParseIntError;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
LockTime::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for LockTime {
|
||||||
|
type Error = ParseIntError;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
LockTime::from_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Height> for LockTime {
|
||||||
|
fn from(h: Height) -> Self {
|
||||||
|
LockTime::Blocks(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Time> for LockTime {
|
||||||
|
fn from(t: Time) -> Self {
|
||||||
|
LockTime::Seconds(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for LockTime {
|
||||||
|
fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
|
||||||
|
use LockTime::*;
|
||||||
|
|
||||||
|
match (*self, *other) {
|
||||||
|
(Blocks(ref a), Blocks(ref b)) => a.partial_cmp(b),
|
||||||
|
(Seconds(ref a), Seconds(ref b)) => a.partial_cmp(b),
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LockTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use LockTime::*;
|
||||||
|
|
||||||
|
if f.alternate() {
|
||||||
|
match *self {
|
||||||
|
Blocks(ref h) => write!(f, "block-height {}", h),
|
||||||
|
Seconds(ref t) => write!(f, "block-time {} (seconds since epoch)", t),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match *self {
|
||||||
|
Blocks(ref h) => fmt::Display::fmt(h, f),
|
||||||
|
Seconds(ref t) => fmt::Display::fmt(t, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for LockTime {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let v = self.to_consensus_u32();
|
||||||
|
v.consensus_encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for LockTime {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||||
|
u32::consensus_decode(r).map(LockTime::from_consensus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An absolute block height, guaranteed to always contain a valid height value.
|
||||||
|
#[derive(Debug, 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(u32);
|
||||||
|
|
||||||
|
impl Height {
|
||||||
|
/// Constructs a new block height.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `n` does not represent a block height value (see documentation on [`LockTime`]).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::blockdata::locktime::Height;
|
||||||
|
///
|
||||||
|
/// let h: u32 = 741521;
|
||||||
|
/// let height = Height::from_consensus(h).expect("invalid height value");
|
||||||
|
/// assert_eq!(height.to_consensus_u32(), h);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_consensus(n: u32) -> Result<Height, Error> {
|
||||||
|
if is_block_height(n) {
|
||||||
|
Ok(Self(n))
|
||||||
|
} else {
|
||||||
|
Err(ConversionError::invalid_height(n).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this `Height` to its inner `u32` value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::LockTime;
|
||||||
|
///
|
||||||
|
/// let n_lock_time: u32 = 741521;
|
||||||
|
/// let lock_time = LockTime::from_consensus(n_lock_time);
|
||||||
|
/// assert!(lock_time.is_block_height());
|
||||||
|
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
|
||||||
|
#[inline]
|
||||||
|
pub fn to_consensus_u32(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Height {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Height {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let n = s.parse::<u32>().map_err(|e| Error::Parse(e, s.to_owned()))?;
|
||||||
|
Height::from_consensus(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Height {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
Height::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Height {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
let n = s.parse::<u32>().map_err(|e| Error::Parse(e, s))?;
|
||||||
|
Height::from_consensus(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
|
||||||
|
///
|
||||||
|
/// Note that there is no manipulation of the inner value during construction or when using
|
||||||
|
/// `to_consensus_u32()`. Said another way, `Time(x)` means 'x seconds since epoch' _not_ '(x -
|
||||||
|
/// threshold) seconds since epoch'.
|
||||||
|
#[derive(Debug, 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(u32);
|
||||||
|
|
||||||
|
impl Time {
|
||||||
|
/// Constructs a new block time.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `n` does not encode a UNIX time stamp (see documentation on [`LockTime`]).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::blockdata::locktime::Time;
|
||||||
|
///
|
||||||
|
/// let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
/// let time = Time::from_consensus(t).expect("invalid time value");
|
||||||
|
/// assert_eq!(time.to_consensus_u32(), t);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_consensus(n: u32) -> Result<Time, Error> {
|
||||||
|
if is_block_time(n) {
|
||||||
|
Ok(Self(n))
|
||||||
|
} else {
|
||||||
|
Err(ConversionError::invalid_time(n).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this `Time` to its inner `u32` value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use bitcoin::LockTime;
|
||||||
|
///
|
||||||
|
/// let n_lock_time: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
/// let lock_time = LockTime::from_consensus(n_lock_time);
|
||||||
|
/// assert_eq!(lock_time.to_consensus_u32(), n_lock_time);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_consensus_u32(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Time {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Time {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let n = s.parse::<u32>().map_err(|e| Error::Parse(e, s.to_owned()))?;
|
||||||
|
Time::from_consensus(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Time {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
Time::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Time {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
let n = s.parse::<u32>().map_err(|e| Error::Parse(e, s))?;
|
||||||
|
Time::from_consensus(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if `n` is a block height i.e., less than 500,000,000.
|
||||||
|
fn is_block_height(n: u32) -> bool {
|
||||||
|
n < LOCK_TIME_THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if `n` is a UNIX timestamp i.e., greater than or equal to 500,000,000.
|
||||||
|
fn is_block_time(n: u32) -> bool {
|
||||||
|
n >= LOCK_TIME_THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Catchall type for errors that relate to time locks.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
/// An error occurred while converting a `u32` to a lock time variant.
|
||||||
|
Conversion(ConversionError),
|
||||||
|
/// An error occurred while operating on lock times.
|
||||||
|
Operation(OperationError),
|
||||||
|
/// An error occurred while parsing a string into an `u32`.
|
||||||
|
Parse(ParseIntError, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Error::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Conversion(ref e) => write_err!(f, "error converting lock time value"; e),
|
||||||
|
Operation(ref e) => write_err!(f, "error during lock time operation"; e),
|
||||||
|
Parse(ref e, ref s) => write_err!(f, "error parsing string: {}", s; e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
Conversion(ref e) => Some(e),
|
||||||
|
Operation(ref e) => Some(e),
|
||||||
|
Parse(ref e, _) => Some(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConversionError> for Error {
|
||||||
|
fn from(e: ConversionError) -> Self {
|
||||||
|
Error::Conversion(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OperationError> for Error {
|
||||||
|
fn from(e: OperationError) -> Self {
|
||||||
|
Error::Operation(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error that occurs when converting a `u32` to a lock time variant.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct ConversionError {
|
||||||
|
/// The expected timelock unit, height (blocks) or time (seconds).
|
||||||
|
unit: LockTimeUnit,
|
||||||
|
/// The invalid input value.
|
||||||
|
input: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConversionError {
|
||||||
|
/// Constructs a `ConversionError` from an invalid `n` when expecting a height value.
|
||||||
|
fn invalid_height(n: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
unit: LockTimeUnit::Blocks,
|
||||||
|
input: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a `ConversionError` from an invalid `n` when expecting a time value.
|
||||||
|
fn invalid_time(n: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
unit: LockTimeUnit::Seconds,
|
||||||
|
input: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConversionError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "invalid lock time value {}, {}", self.input, self.unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
|
impl std::error::Error for ConversionError {}
|
||||||
|
|
||||||
|
/// Describes the two types of locking, lock-by-blockheight and lock-by-blocktime.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
enum LockTimeUnit {
|
||||||
|
/// Lock by blockheight.
|
||||||
|
Blocks,
|
||||||
|
/// Lock by blocktime.
|
||||||
|
Seconds,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LockTimeUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use LockTimeUnit::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Blocks => write!(f, "expected lock-by-blockheight (must be < {})", LOCK_TIME_THRESHOLD),
|
||||||
|
Seconds => write!(f, "expected lock-by-blocktime (must be >= {})", LOCK_TIME_THRESHOLD),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors than occur when operating on lock times.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum OperationError {
|
||||||
|
/// Cannot compare different lock time units (height vs time).
|
||||||
|
InvalidComparison,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for OperationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::OperationError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
InvalidComparison => f.write_str("cannot compare different lock units (height vs time)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
|
impl std::error::Error for OperationError {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_and_alternate() {
|
||||||
|
let n = LockTime::from_consensus(100);
|
||||||
|
let s = format!("{}", n);
|
||||||
|
assert_eq!(&s, "100");
|
||||||
|
|
||||||
|
let got = format!("{:#}", n);
|
||||||
|
assert_eq!(got, "block-height 100");
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod locktime;
|
||||||
pub mod opcodes;
|
pub mod opcodes;
|
||||||
pub mod script;
|
pub mod script;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
|
@ -26,6 +26,7 @@ use crate::blockdata::constants::{WITNESS_SCALE_FACTOR, MAX_SEQUENCE};
|
||||||
#[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::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;
|
||||||
|
@ -206,6 +207,20 @@ pub struct TxIn {
|
||||||
pub witness: Witness
|
pub witness: Witness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TxIn {
|
||||||
|
/// Returns true if this input enables the [`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
|
||||||
|
/// ignored. If this returns false and OP_CHECKLOCKTIMEVERIFY is used in the redeem script with
|
||||||
|
/// this input then the script execution will fail [BIP-0065].
|
||||||
|
///
|
||||||
|
/// [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
|
||||||
|
pub fn enables_lock_time(&self) -> bool {
|
||||||
|
self.sequence != Sequence::MAX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for TxIn {
|
impl Default for TxIn {
|
||||||
fn default() -> TxIn {
|
fn default() -> TxIn {
|
||||||
TxIn {
|
TxIn {
|
||||||
|
@ -554,8 +569,13 @@ impl<E> EncodeSigningDataResult<E> {
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
/// The protocol version, is currently expected to be 1 or 2 (BIP 68).
|
/// The protocol version, is currently expected to be 1 or 2 (BIP 68).
|
||||||
pub version: i32,
|
pub version: i32,
|
||||||
/// Block number before which this transaction is valid, or 0 for valid immediately.
|
/// Block height or timestamp. Transaction cannot be included in a block until this height/time.
|
||||||
pub lock_time: u32,
|
///
|
||||||
|
/// ### Relevant BIPs
|
||||||
|
///
|
||||||
|
/// * [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,
|
||||||
/// List of transaction inputs.
|
/// List of transaction inputs.
|
||||||
pub input: Vec<TxIn>,
|
pub input: Vec<TxIn>,
|
||||||
/// List of transaction outputs.
|
/// List of transaction outputs.
|
||||||
|
@ -916,6 +936,27 @@ impl Transaction {
|
||||||
pub fn is_explicitly_rbf(&self) -> bool {
|
pub fn is_explicitly_rbf(&self) -> bool {
|
||||||
self.input.iter().any(|input| input.sequence.is_rbf())
|
self.input.iter().any(|input| input.sequence.is_rbf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this [`Transaction`]'s absolute timelock is satisfied at `height`/`time`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// By definition if the lock time is not enabled the transaction's absolute timelock is
|
||||||
|
/// considered to be satisfied i.e., there are no timelock constraints restricting this
|
||||||
|
/// transaction from being mined immediately.
|
||||||
|
pub fn is_absolute_timelock_satisfied(&self, height: Height, time: Time) -> bool {
|
||||||
|
if !self.is_lock_time_enabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LockTime::from(self.lock_time).is_satisfied_by(height, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this transactions nLockTime is enabled ([BIP-65]).
|
||||||
|
///
|
||||||
|
/// [BIP-65]: https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
|
||||||
|
pub fn is_lock_time_enabled(&self) -> bool {
|
||||||
|
self.input.iter().any(|i| i.enables_lock_time())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
||||||
|
@ -1224,6 +1265,7 @@ mod tests {
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
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::consensus::encode::serialize;
|
use crate::consensus::encode::serialize;
|
||||||
use crate::consensus::encode::deserialize;
|
use crate::consensus::encode::deserialize;
|
||||||
|
|
||||||
|
@ -1324,7 +1366,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, 0);
|
assert_eq!(realtx.lock_time, PackedLockTime::ZERO);
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", realtx.txid()),
|
assert_eq!(format!("{:x}", realtx.txid()),
|
||||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||||
|
@ -1358,7 +1400,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, 0);
|
assert_eq!(realtx.lock_time, PackedLockTime::ZERO);
|
||||||
|
|
||||||
assert_eq!(format!("{:x}", realtx.txid()),
|
assert_eq!(format!("{:x}", realtx.txid()),
|
||||||
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
|
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
|
||||||
|
@ -1552,7 +1594,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: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![TxIn::default(), TxIn::default()],
|
input: vec![TxIn::default(), TxIn::default()],
|
||||||
output: vec![TxOut::default()],
|
output: vec![TxOut::default()],
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,6 +94,7 @@ use std::io;
|
||||||
use core2::io;
|
use core2::io;
|
||||||
|
|
||||||
pub use crate::blockdata::block::{Block, BlockHeader};
|
pub use crate::blockdata::block::{Block, BlockHeader};
|
||||||
|
pub use crate::blockdata::locktime::{self, LockTime, PackedLockTime};
|
||||||
pub use crate::blockdata::script::Script;
|
pub use crate::blockdata::script::Script;
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub use crate::blockdata::transaction::SigHashType;
|
pub use crate::blockdata::transaction::SigHashType;
|
||||||
|
|
|
@ -14,6 +14,7 @@ use crate::hash_types::Sighash;
|
||||||
use crate::blockdata::script::Script;
|
use crate::blockdata::script::Script;
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
use crate::blockdata::transaction::{Transaction, TxIn, EcdsaSighashType};
|
use crate::blockdata::transaction::{Transaction, TxIn, EcdsaSighashType};
|
||||||
|
use crate::blockdata::locktime::LockTime;
|
||||||
use crate::consensus::{encode, Encodable};
|
use crate::consensus::{encode, Encodable};
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
|
@ -26,7 +27,7 @@ use crate::util::sighash;
|
||||||
#[deprecated(since = "0.24.0", note = "please use [sighash::SighashCache] instead")]
|
#[deprecated(since = "0.24.0", note = "please use [sighash::SighashCache] instead")]
|
||||||
pub struct SighashComponents {
|
pub struct SighashComponents {
|
||||||
tx_version: i32,
|
tx_version: i32,
|
||||||
tx_locktime: u32,
|
tx_locktime: LockTime,
|
||||||
/// Hash of all the previous outputs
|
/// Hash of all the previous outputs
|
||||||
pub hash_prevouts: Sighash,
|
pub hash_prevouts: Sighash,
|
||||||
/// Hash of all the input sequence nos
|
/// Hash of all the input sequence nos
|
||||||
|
@ -68,7 +69,7 @@ impl SighashComponents {
|
||||||
|
|
||||||
SighashComponents {
|
SighashComponents {
|
||||||
tx_version: tx.version,
|
tx_version: tx.version,
|
||||||
tx_locktime: tx.lock_time,
|
tx_locktime: tx.lock_time.into(),
|
||||||
hash_prevouts,
|
hash_prevouts,
|
||||||
hash_sequence,
|
hash_sequence,
|
||||||
hash_outputs,
|
hash_outputs,
|
||||||
|
@ -156,9 +157,9 @@ impl<R: DerefMut<Target = Transaction>> SigHashCache<R> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
||||||
/// use bitcoin::util::bip143::SigHashCache;
|
/// use bitcoin::util::bip143::SigHashCache;
|
||||||
/// use bitcoin::Script;
|
/// use bitcoin::{PackedLockTime, Script};
|
||||||
///
|
///
|
||||||
/// let mut tx_to_sign = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
|
/// let mut tx_to_sign = Transaction { version: 2, lock_time: 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);
|
||||||
|
@ -181,6 +182,7 @@ mod tests {
|
||||||
use crate::hash_types::Sighash;
|
use crate::hash_types::Sighash;
|
||||||
use crate::blockdata::script::Script;
|
use crate::blockdata::script::Script;
|
||||||
use crate::blockdata::transaction::Transaction;
|
use crate::blockdata::transaction::Transaction;
|
||||||
|
use crate::blockdata::locktime::LockTime;
|
||||||
use crate::consensus::encode::deserialize;
|
use crate::consensus::encode::deserialize;
|
||||||
use crate::network::constants::Network;
|
use crate::network::constants::Network;
|
||||||
use crate::util::address::Address;
|
use crate::util::address::Address;
|
||||||
|
@ -225,7 +227,7 @@ mod tests {
|
||||||
comp,
|
comp,
|
||||||
SighashComponents {
|
SighashComponents {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 17,
|
tx_locktime: LockTime::from_consensus(17),
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
Sighash, "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
|
Sighash, "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
|
||||||
),
|
),
|
||||||
|
@ -261,7 +263,7 @@ mod tests {
|
||||||
comp,
|
comp,
|
||||||
SighashComponents {
|
SighashComponents {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 1170,
|
tx_locktime: LockTime::from_consensus(1170),
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
Sighash, "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
|
Sighash, "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
|
||||||
),
|
),
|
||||||
|
@ -304,7 +306,7 @@ mod tests {
|
||||||
comp,
|
comp,
|
||||||
SighashComponents {
|
SighashComponents {
|
||||||
tx_version: 1,
|
tx_version: 1,
|
||||||
tx_locktime: 0,
|
tx_locktime: LockTime::ZERO,
|
||||||
hash_prevouts: hex_hash!(
|
hash_prevouts: hex_hash!(
|
||||||
Sighash, "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
|
Sighash, "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
|
||||||
),
|
),
|
||||||
|
|
|
@ -366,13 +366,13 @@ mod test {
|
||||||
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,
|
TxOut, Txid, Witness, LockTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
version: 1,
|
version: 1,
|
||||||
lock_time: 2,
|
lock_time: 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(),
|
||||||
|
|
|
@ -335,6 +335,7 @@ impl Decodable for PartiallySignedTransaction {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::blockdata::locktime::PackedLockTime;
|
||||||
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;
|
||||||
|
@ -358,7 +359,7 @@ mod tests {
|
||||||
let psbt = PartiallySignedTransaction {
|
let psbt = PartiallySignedTransaction {
|
||||||
unsigned_tx: Transaction {
|
unsigned_tx: Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
},
|
},
|
||||||
|
@ -428,7 +429,7 @@ mod tests {
|
||||||
let expected = PartiallySignedTransaction {
|
let expected = PartiallySignedTransaction {
|
||||||
unsigned_tx: Transaction {
|
unsigned_tx: Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 1257139,
|
lock_time: PackedLockTime(1257139),
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: Txid::from_hex(
|
txid: Txid::from_hex(
|
||||||
|
@ -501,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: 0,
|
lock_time: 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(),
|
||||||
|
@ -599,6 +600,7 @@ mod tests {
|
||||||
use crate::blockdata::script::Script;
|
use crate::blockdata::script::Script;
|
||||||
use crate::blockdata::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, OutPoint, Sequence};
|
use crate::blockdata::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, OutPoint, Sequence};
|
||||||
use crate::consensus::encode::serialize_hex;
|
use crate::consensus::encode::serialize_hex;
|
||||||
|
use crate::blockdata::locktime::PackedLockTime;
|
||||||
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};
|
||||||
|
@ -687,7 +689,7 @@ mod tests {
|
||||||
let unserialized = PartiallySignedTransaction {
|
let unserialized = PartiallySignedTransaction {
|
||||||
unsigned_tx: Transaction {
|
unsigned_tx: Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 1257139,
|
lock_time: PackedLockTime(1257139),
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: Txid::from_hex(
|
txid: Txid::from_hex(
|
||||||
|
@ -718,7 +720,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: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: Txid::from_hex(
|
txid: Txid::from_hex(
|
||||||
|
@ -999,7 +1001,7 @@ mod tests {
|
||||||
let mut unserialized = PartiallySignedTransaction {
|
let mut unserialized = PartiallySignedTransaction {
|
||||||
unsigned_tx: Transaction {
|
unsigned_tx: Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 1257139,
|
lock_time: PackedLockTime(1257139),
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: Txid::from_hex(
|
txid: Txid::from_hex(
|
||||||
|
@ -1030,7 +1032,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: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint {
|
previous_output: OutPoint {
|
||||||
txid: Txid::from_hex(
|
txid: Txid::from_hex(
|
||||||
|
|
|
@ -759,9 +759,9 @@ impl<R: DerefMut<Target=Transaction>> SighashCache<R> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
||||||
/// use bitcoin::util::sighash::SighashCache;
|
/// use bitcoin::util::sighash::SighashCache;
|
||||||
/// use bitcoin::Script;
|
/// use bitcoin::{PackedLockTime, Script};
|
||||||
///
|
///
|
||||||
/// let mut tx_to_sign = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
|
/// let mut tx_to_sign = Transaction { version: 2, lock_time: 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);
|
||||||
|
@ -812,6 +812,7 @@ impl<'a> Encodable for Annex<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::blockdata::locktime::PackedLockTime;
|
||||||
use crate::consensus::deserialize;
|
use crate::consensus::deserialize;
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::hashes::{Hash, HashEngine};
|
use crate::hashes::{Hash, HashEngine};
|
||||||
|
@ -958,7 +959,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: 0,
|
lock_time: PackedLockTime::ZERO,
|
||||||
input: vec![TxIn::default()],
|
input: vec![TxIn::default()],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue