Merge rust-bitcoin/rust-bitcoin#2991: Move locktimes and `Sequence` to `primitives`

64c31cfb97 Move locktimes and Sequence to primitives (Tobin C. Harding)

Pull request description:

  The `absolute` and `relative` locktimes as well as the `Sequence` are all primitive bitcoin types.

  Move the `Sequence`, and `locktime` stuff over to `primitives`.

  There is nothing surprising here, the consensus encoding stuff stays in `bitcoin` and we re-export everything from `blockdata`.

  Note please `Sequence` is no longer publicly available at `bitcoin::transaction::Sequence` but it is available at `bitcoin::Sequence`.

ACKs for top commit:
  Kixunil:
    ACK 64c31cfb97
  apoelstra:
    ACK 64c31cfb97

Tree-SHA512: 968aa595bfb53e8fcfa860dae797ec5381ed49c67326a8ef9494086ec65d737502dffe4b24143e159042d07c59c5109d50103029082f87e1c3c26671e975f1b3
This commit is contained in:
merge-script 2024-07-15 21:13:41 +00:00
commit 6d483585df
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
14 changed files with 348 additions and 269 deletions

View File

@ -101,6 +101,10 @@ name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",
"mutagen",
"ordered",
"serde",
]

View File

@ -100,6 +100,10 @@ name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",
"mutagen",
"ordered",
"serde",
]

View File

@ -16,9 +16,9 @@ use crate::locktime::absolute;
use crate::network::Network;
use crate::opcodes::all::*;
use crate::pow::CompactTarget;
use crate::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::transaction::{self, OutPoint, Transaction, TxIn, TxOut};
use crate::witness::Witness;
use crate::{script, Amount, BlockHash};
use crate::{script, Amount, BlockHash, Sequence};
/// How many seconds between blocks we expect on average.
pub const TARGET_BLOCK_SPACING: u32 = 600;

View File

@ -7,7 +7,6 @@
pub mod block;
pub mod constants;
pub mod locktime;
pub mod script;
pub mod transaction;
pub mod witness;
@ -47,6 +46,49 @@ pub mod fee_rate {
}
}
/// Provides absolute and relative locktimes.
pub mod locktime {
pub mod absolute {
//! 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 io::{BufRead, Write};
pub use crate::consensus::encode::{self, Decodable, Encodable};
/// Re-export everything from the `primitives::locktime::absolute` module.
#[rustfmt::skip] // Keep public re-exports separate.
pub use primitives::locktime::absolute::*;
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: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
u32::consensus_decode(r).map(LockTime::from_consensus)
}
}
}
pub mod relative {
//! 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.
/// Re-export everything from the `primitives::locktime::relative` module.
pub use primitives::locktime::relative::*;
}
}
/// Bitcoin script opcodes.
pub mod opcodes {
/// Re-export everything from the [`primitives::opcodes`] module.

View File

@ -7,7 +7,7 @@ use crate::locktime::absolute;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::prelude::Vec;
use crate::transaction::Sequence;
use crate::Sequence;
/// An Object which can be used to construct a script piece by piece.
#[derive(PartialEq, Eq, Clone)]

View File

@ -15,13 +15,13 @@ use core::{cmp, fmt, str};
use hashes::sha256d;
use internals::write_err;
use io::{BufRead, Write};
use units::parse::{self, PrefixedHexError, UnprefixedHexError};
use primitives::Sequence;
use units::parse;
use super::Weight;
use crate::consensus::{encode, Decodable, Encodable};
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
use crate::locktime::absolute::{self, Height, Time};
use crate::locktime::relative::{self, TimeOverflowError};
use crate::prelude::{Borrow, Vec};
use crate::script::{Script, ScriptBuf};
#[cfg(doc)]
@ -330,221 +330,6 @@ impl Default for TxIn {
}
}
/// Bitcoin transaction input sequence number.
///
/// The sequence field is used for:
/// - Indicating whether absolute lock-time (specified in `lock_time` field of [`Transaction`])
/// is enabled.
/// - Indicating and encoding [BIP-68] relative lock-times.
/// - Indicating whether a transaction opts-in to [BIP-125] replace-by-fee.
///
/// Note that transactions spending an output with `OP_CHECKLOCKTIMEVERIFY`MUST NOT use
/// `Sequence::MAX` for the corresponding input. [BIP-65]
///
/// [BIP-65]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
/// [BIP-68]: <https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki>
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki>
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Sequence(pub u32);
impl Sequence {
/// The maximum allowable sequence number.
///
/// 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 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.
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.
pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD);
/// The number of bytes that a sequence number contributes to the size of a transaction.
const SIZE: usize = 4; // Serialized length of a u32.
/// The lowest sequence number that does not opt-in for replace-by-fee.
///
/// A transaction is considered to have opted in to replacement of itself
/// if any of it's inputs have a `Sequence` number less than this value
/// (Explicit Signalling [BIP-125]).
///
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]>
const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE);
/// BIP-68 relative lock time disable flag mask.
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000;
/// BIP-68 relative lock time type flag mask.
const LOCK_TYPE_MASK: u32 = 0x00400000;
/// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
#[inline]
pub fn enables_absolute_lock_time(&self) -> bool { *self != Sequence::MAX }
/// Returns `true` if the sequence number indicates that the transaction is finalized.
///
/// Instead of this method please consider using `!enables_absolute_lock_time` because it
/// is equivalent and improves readability for those not steeped in Bitcoin folklore.
///
/// ## Historical note
///
/// The term 'final' is an archaic Bitcoin term, it may have come about because the sequence
/// number in the original Bitcoin code was intended to be incremented in order to replace a
/// transaction, so once the sequence number got to `u64::MAX` it could no longer be increased,
/// hence it was 'final'.
///
///
/// Some other references to the term:
/// - `CTxIn::SEQUENCE_FINAL` in the Bitcoin Core code.
/// - [BIP-112]: "BIP 68 prevents a non-final transaction from being selected for inclusion in a
/// block until the corresponding input has reached the specified age"
///
/// [BIP-112]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
#[inline]
pub fn is_final(&self) -> bool { !self.enables_absolute_lock_time() }
/// Returns true if the transaction opted-in to BIP125 replace-by-fee.
///
/// Replace by fee is signaled by the sequence being less than 0xfffffffe which is checked by
/// this method. Note, this is the highest "non-final" value (see [`Sequence::is_final`]).
#[inline]
pub fn is_rbf(&self) -> bool { *self < Sequence::MIN_NO_RBF }
/// Returns `true` if the sequence has a relative lock-time.
#[inline]
pub fn is_relative_lock_time(&self) -> bool {
self.0 & Sequence::LOCK_TIME_DISABLE_FLAG_MASK == 0
}
/// Returns `true` if the sequence number encodes a block based relative lock-time.
#[inline]
pub fn is_height_locked(&self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK == 0)
}
/// Returns `true` if the sequence number encodes a time interval based relative lock-time.
#[inline]
pub fn is_time_locked(&self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
}
/// Creates a `Sequence` from a prefixed hex string.
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let lock_time = parse::hex_u32_prefixed(s)?;
Ok(Self::from_consensus(lock_time))
}
/// Creates a `Sequence` from an unprefixed hex string.
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
let lock_time = parse::hex_u32_unprefixed(s)?;
Ok(Self::from_consensus(lock_time))
}
/// Creates a relative lock-time using block height.
#[inline]
pub fn from_height(height: u16) -> Self { Sequence(u32::from(height)) }
/// Creates a relative lock-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 {
Sequence(u32::from(intervals) | Sequence::LOCK_TYPE_MASK)
}
/// Creates a relative lock-time from seconds, converting the seconds into 512 second
/// interval with floor division.
///
/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
pub fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
if let Ok(interval) = u16::try_from(seconds / 512) {
Ok(Sequence::from_512_second_intervals(interval))
} else {
Err(TimeOverflowError::new(seconds))
}
}
/// Creates a relative lock-time from seconds, converting the seconds into 512 second
/// interval with ceiling division.
///
/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
Ok(Sequence::from_512_second_intervals(interval))
} else {
Err(TimeOverflowError::new(seconds))
}
}
/// Creates a sequence from a u32 value.
#[inline]
pub fn from_consensus(n: u32) -> Self { Sequence(n) }
/// Returns the inner 32bit integer value of Sequence.
#[inline]
pub fn to_consensus_u32(self) -> u32 { 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::{Height, LockTime, 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 {
/// The default value of sequence is 0xffffffff.
fn default() -> Self { Sequence::MAX }
}
impl From<Sequence> for u32 {
fn from(sequence: Sequence) -> u32 { sequence.0 }
}
impl fmt::Display for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
impl fmt::LowerHex for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl fmt::UpperHex for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
}
impl fmt::Debug for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 10 because its 8 digits + 2 for the '0x'
write!(f, "Sequence({:#010x})", self.0)
}
}
units::impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);
/// Bitcoin transaction output.
///
/// Defines new coins to be created as a result of the transaction,

View File

@ -119,7 +119,7 @@ pub use crate::{
blockdata::script::witness_program::{self, WitnessProgram},
blockdata::script::witness_version::{self, WitnessVersion},
blockdata::script::{self, Script, ScriptBuf, ScriptHash, WScriptHash},
blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut, Txid, Wtxid},
blockdata::transaction::{self, OutPoint, Transaction, TxIn, TxOut, Txid, Wtxid},
blockdata::weight::Weight,
blockdata::witness::{self, Witness},
consensus::encode::VarInt,
@ -134,6 +134,8 @@ pub use crate::{
sighash::{EcdsaSighashType, TapSighashType},
taproot::{TapBranchTag, TapLeafHash, TapLeafTag, TapNodeHash, TapTweakHash, TapTweakTag},
};
#[doc(inline)]
pub use primitives::Sequence;
pub use units::{BlockHeight, BlockInterval};
#[rustfmt::skip]

View File

@ -1217,8 +1217,9 @@ mod tests {
use crate::network::NetworkKind;
use crate::psbt::serialize::{Deserialize, Serialize};
use crate::script::ScriptBuf;
use crate::transaction::{self, OutPoint, Sequence, TxIn};
use crate::transaction::{self, OutPoint, TxIn};
use crate::witness::Witness;
use crate::Sequence;
#[track_caller]
pub fn hex_psbt(s: &str) -> Result<Psbt, crate::psbt::error::Error> {

View File

@ -16,18 +16,28 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
std = ["alloc", "internals/std"]
alloc = ["internals/alloc"]
serde = ["actual-serde", "internals/serde"]
std = ["alloc", "internals/std", "io/std", "units/std"]
alloc = ["internals/alloc", "io/alloc", "units/alloc"]
serde = ["actual-serde", "internals/serde", "units/serde"]
[dependencies]
internals = { package = "bitcoin-internals", version = "0.3.0" }
io = { package = "bitcoin-io", version = "0.1.1", default-features = false }
units = { package = "bitcoin-units", version = "0.1.0", default-features = false }
ordered = { version = "0.2.0", optional = true }
# Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0.103", default-features = false, optional = true }
actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = ["derive"], optional = true }
[dev-dependencies]
[target.'cfg(mutate)'.dev-dependencies]
mutagen = { git = "https://github.com/llogiq/mutagen" }
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(mutate)'] }

View File

@ -8,8 +8,6 @@
//!
//! [`rust-bitcoin`]: <https://github.com/rust-bitcoin>
// NB: This crate is empty if `alloc` is not enabled.
#![cfg(feature = "alloc")]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
@ -26,9 +24,22 @@ extern crate alloc;
extern crate std;
#[cfg(feature = "serde")]
#[macro_use]
extern crate actual_serde as serde;
#[cfg(feature = "alloc")]
pub mod locktime;
pub mod opcodes;
pub mod sequence;
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
#[cfg(feature = "alloc")]
pub use self::{
locktime::{absolute, relative},
};
#[doc(inline)]
pub use self::sequence::Sequence;
#[rustfmt::skip]
#[allow(unused_imports)]

View File

@ -8,14 +8,12 @@
use core::cmp::Ordering;
use core::fmt;
use io::{BufRead, Write};
#[cfg(all(test, mutate))]
use mutagen::mutate;
use units::parse::{self, PrefixedHexError, UnprefixedHexError};
#[cfg(doc)]
use crate::absolute;
use crate::consensus::encode::{self, Decodable, Encodable};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
@ -26,14 +24,14 @@ pub use units::locktime::absolute::{
/// 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 `Transaction::lock_time`
/// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`.
///
/// ### Note on ordering
///
/// Locktimes may be height- or time-based, and these metrics are incommensurate; there is no total
/// ordering on locktimes. We therefore have implemented [`PartialOrd`] but not [`Ord`].
/// For [`crate::Transaction`], which has a locktime field, we implement a total ordering to make
/// For `Transaction`, which has a locktime field, we implement a total ordering to make
/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
/// consensus encoding to order it. We also implement [`ordered::ArbitraryOrd`] if the "ordered"
/// feature is enabled.
@ -46,7 +44,7 @@ pub use units::locktime::absolute::{
/// # Examples
///
/// ```
/// # use bitcoin::absolute::{LockTime, LockTime::*};
/// # use bitcoin_primitives::absolute::{LockTime, LockTime::*};
/// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = LockTime::from_consensus(741521); // nLockTime
/// // To compare absolute lock times there are various `is_satisfied_*` methods, you may also use:
@ -63,7 +61,7 @@ pub enum LockTime {
/// # Examples
///
/// ```rust
/// use bitcoin::absolute::LockTime;
/// use bitcoin_primitives::absolute::LockTime;
///
/// let block: u32 = 741521;
/// let n = LockTime::from_height(block).expect("valid height");
@ -76,7 +74,7 @@ pub enum LockTime {
/// # Examples
///
/// ```rust
/// use bitcoin::absolute::LockTime;
/// use bitcoin_primitives::absolute::LockTime;
///
/// let seconds: u32 = 1653195600; // May 22nd, 5am UTC.
/// let n = LockTime::from_time(seconds).expect("valid time");
@ -87,7 +85,7 @@ pub enum LockTime {
}
impl LockTime {
/// If [`crate::Transaction::lock_time`] is set to zero it is ignored, in other words a
/// If `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::ZERO);
@ -111,7 +109,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::absolute::LockTime;
/// # use bitcoin_primitives::absolute::LockTime;
/// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
///
/// // `from_consensus` roundtrips as expected with `to_consensus_u32`.
@ -140,7 +138,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::absolute::LockTime;
/// # use bitcoin_primitives::absolute::LockTime;
/// assert!(LockTime::from_height(741521).is_ok());
/// assert!(LockTime::from_height(1653195600).is_err());
/// ```
@ -166,7 +164,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::absolute::LockTime;
/// # use bitcoin_primitives::absolute::LockTime;
/// assert!(LockTime::from_time(1653195600).is_ok());
/// assert!(LockTime::from_time(741521).is_err());
/// ```
@ -200,13 +198,13 @@ impl LockTime {
/// 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
/// is satisfied if a transaction with nLockTime (`Transaction::lock_time`) set to
/// `height`/`time` is valid.
///
/// # Examples
///
/// ```no_run
/// # use bitcoin::absolute::{LockTime, Height, Time};
/// # use bitcoin_primitives::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") }
@ -241,7 +239,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::absolute::{LockTime, LockTime::*};
/// # use bitcoin_primitives::absolute::{LockTime, LockTime::*};
/// let lock_time = LockTime::from_consensus(741521);
/// let check = LockTime::from_consensus(741521 + 1);
/// assert!(lock_time.is_implied_by(check));
@ -270,7 +268,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::absolute::{LockTime, LockTime::*};
/// # use bitcoin_primitives::absolute::{LockTime, LockTime::*};
/// # let n = LockTime::from_consensus(741521); // n OP_CHECKLOCKTIMEVERIFY
/// # let lock_time = LockTime::from_consensus(741521 + 1); // nLockTime
///
@ -346,21 +344,6 @@ impl fmt::Display for LockTime {
}
}
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: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
u32::consensus_decode(r).map(LockTime::from_consensus)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for LockTime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>

View File

@ -22,7 +22,7 @@ pub use units::locktime::relative::{Height, Time, TimeOverflowError};
/// A relative lock time value, representing either a block height or time (512 second intervals).
///
/// Used for sequence numbers (`nSequence` in Bitcoin Core and [`crate::TxIn::sequence`]
/// Used for sequence numbers (`nSequence` in Bitcoin Core and `TxIn::sequence`
/// in this library) and also for the argument to opcode 'OP_CHECKSEQUENCEVERIFY`.
///
/// ### Note on ordering
@ -62,7 +62,7 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::relative::LockTime;
/// # use bitcoin_primitives::relative::LockTime;
///
/// // `from_consensus` roundtrips with `to_consensus_u32` for small values.
/// let n_lock_time: u32 = 7000;
@ -165,8 +165,8 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
/// # use bitcoin_primitives::Sequence;
/// # use bitcoin_primitives::locktime::relative::{LockTime, Height, Time};
///
/// # let required_height = 100; // 100 blocks.
/// # let intervals = 70; // Approx 10 hours.
@ -205,8 +205,8 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
/// # use bitcoin_primitives::Sequence;
/// # use bitcoin_primitives::locktime::relative::{LockTime, Height, Time};
///
/// # let required_height = 100; // 100 blocks.
/// # let lock = Sequence::from_height(required_height).to_relative_lock_time().expect("valid height");
@ -253,8 +253,8 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
/// # use bitcoin_primitives::Sequence;
/// # use bitcoin_primitives::locktime::relative::{LockTime, Height, Time};
///
/// let required_height: u16 = 100;
/// let lock = Sequence::from_height(required_height).to_relative_lock_time().expect("valid height");
@ -280,8 +280,8 @@ impl LockTime {
/// # Examples
///
/// ```rust
/// # use bitcoin::Sequence;
/// # use bitcoin::locktime::relative::{LockTime, Height, Time};
/// # use bitcoin_primitives::Sequence;
/// # use bitcoin_primitives::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");

237
primitives/src/sequence.rs Normal file
View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: CC0-1.0
//! Bitcoin transaction input sequence number.
//!
//! The sequence field is used for:
//! - Indicating whether absolute lock-time (specified in `lock_time` field of `Transaction`)
//! is enabled.
//! - Indicating and encoding [BIP-68] relative lock-times.
//! - Indicating whether a transaction opts-in to [BIP-125] replace-by-fee.
//!
//! Note that transactions spending an output with `OP_CHECKLOCKTIMEVERIFY`MUST NOT use
//! `Sequence::MAX` for the corresponding input. [BIP-65]
//!
//! [BIP-65]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
//! [BIP-68]: <https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki>
//! [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki>
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "alloc")]
use units::locktime::relative::TimeOverflowError;
#[cfg(feature = "alloc")]
use units::parse::{self, PrefixedHexError, UnprefixedHexError};
#[cfg(feature = "alloc")]
use crate::locktime::relative;
/// Bitcoin transaction input sequence number.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Sequence(pub u32);
impl Sequence {
/// The maximum allowable sequence number.
///
/// 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 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.
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.
pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD);
/// The number of bytes that a sequence number contributes to the size of a transaction.
pub const SIZE: usize = 4; // Serialized length of a u32.
/// The lowest sequence number that does not opt-in for replace-by-fee.
///
/// A transaction is considered to have opted in to replacement of itself
/// if any of it's inputs have a `Sequence` number less than this value
/// (Explicit Signalling [BIP-125]).
///
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]>
const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE);
/// BIP-68 relative lock time disable flag mask.
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000;
/// BIP-68 relative lock time type flag mask.
const LOCK_TYPE_MASK: u32 = 0x00400000;
/// Returns `true` if the sequence number enables absolute lock-time (`Transaction::lock_time`).
#[inline]
pub fn enables_absolute_lock_time(&self) -> bool { *self != Sequence::MAX }
/// Returns `true` if the sequence number indicates that the transaction is finalized.
///
/// Instead of this method please consider using `!enables_absolute_lock_time` because it
/// is equivalent and improves readability for those not steeped in Bitcoin folklore.
///
/// ## Historical note
///
/// The term 'final' is an archaic Bitcoin term, it may have come about because the sequence
/// number in the original Bitcoin code was intended to be incremented in order to replace a
/// transaction, so once the sequence number got to `u64::MAX` it could no longer be increased,
/// hence it was 'final'.
///
///
/// Some other references to the term:
/// - `CTxIn::SEQUENCE_FINAL` in the Bitcoin Core code.
/// - [BIP-112]: "BIP 68 prevents a non-final transaction from being selected for inclusion in a
/// block until the corresponding input has reached the specified age"
///
/// [BIP-112]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
#[inline]
pub fn is_final(&self) -> bool { !self.enables_absolute_lock_time() }
/// Returns true if the transaction opted-in to BIP125 replace-by-fee.
///
/// Replace by fee is signaled by the sequence being less than 0xfffffffe which is checked by
/// this method. Note, this is the highest "non-final" value (see [`Sequence::is_final`]).
#[inline]
pub fn is_rbf(&self) -> bool { *self < Sequence::MIN_NO_RBF }
/// Returns `true` if the sequence has a relative lock-time.
#[inline]
pub fn is_relative_lock_time(&self) -> bool {
self.0 & Sequence::LOCK_TIME_DISABLE_FLAG_MASK == 0
}
/// Returns `true` if the sequence number encodes a block based relative lock-time.
#[inline]
pub fn is_height_locked(&self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK == 0)
}
/// Returns `true` if the sequence number encodes a time interval based relative lock-time.
#[inline]
pub fn is_time_locked(&self) -> bool {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
}
/// Creates a `Sequence` from a prefixed hex string.
#[cfg(feature = "alloc")]
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let lock_time = parse::hex_u32_prefixed(s)?;
Ok(Self::from_consensus(lock_time))
}
/// Creates a `Sequence` from an unprefixed hex string.
#[cfg(feature = "alloc")]
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
let lock_time = parse::hex_u32_unprefixed(s)?;
Ok(Self::from_consensus(lock_time))
}
/// Creates a relative lock-time using block height.
#[inline]
pub fn from_height(height: u16) -> Self { Sequence(u32::from(height)) }
/// Creates a relative lock-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 {
Sequence(u32::from(intervals) | Sequence::LOCK_TYPE_MASK)
}
/// Creates a relative lock-time from seconds, converting the seconds into 512 second
/// interval with floor division.
///
/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
#[cfg(feature = "alloc")]
pub fn from_seconds_floor(seconds: u32) -> Result<Self, TimeOverflowError> {
if let Ok(interval) = u16::try_from(seconds / 512) {
Ok(Sequence::from_512_second_intervals(interval))
} else {
Err(TimeOverflowError::new(seconds))
}
}
/// Creates a relative lock-time from seconds, converting the seconds into 512 second
/// interval with ceiling division.
///
/// Will return an error if the input cannot be encoded in 16 bits.
#[inline]
#[cfg(feature = "alloc")]
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, TimeOverflowError> {
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
Ok(Sequence::from_512_second_intervals(interval))
} else {
Err(TimeOverflowError::new(seconds))
}
}
/// Creates a sequence from a u32 value.
#[inline]
pub fn from_consensus(n: u32) -> Self { Sequence(n) }
/// Returns the inner 32bit integer value of Sequence.
#[inline]
pub fn to_consensus_u32(self) -> u32 { self.0 }
/// Creates a [`relative::LockTime`] from this [`Sequence`] number.
#[inline]
#[cfg(feature = "alloc")]
pub fn to_relative_lock_time(&self) -> Option<relative::LockTime> {
use crate::locktime::relative::{Height, LockTime, 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.
#[cfg(feature = "alloc")]
fn low_u16(&self) -> u16 { self.0 as u16 }
}
impl Default for Sequence {
/// The default value of sequence is 0xffffffff.
fn default() -> Self { Sequence::MAX }
}
impl From<Sequence> for u32 {
fn from(sequence: Sequence) -> u32 { sequence.0 }
}
impl fmt::Display for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
impl fmt::LowerHex for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl fmt::UpperHex for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
}
impl fmt::Debug for Sequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 10 because its 8 digits + 2 for the '0x'
write!(f, "Sequence({:#010x})", self.0)
}
}
#[cfg(feature = "alloc")]
units::impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);