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: ACK64c31cfb97
apoelstra: ACK64c31cfb97
Tree-SHA512: 968aa595bfb53e8fcfa860dae797ec5381ed49c67326a8ef9494086ec65d737502dffe4b24143e159042d07c59c5109d50103029082f87e1c3c26671e975f1b3
This commit is contained in:
commit
6d483585df
|
@ -101,6 +101,10 @@ name = "bitcoin-primitives"
|
|||
version = "0.100.0"
|
||||
dependencies = [
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"mutagen",
|
||||
"ordered",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
|
|
@ -100,6 +100,10 @@ name = "bitcoin-primitives"
|
|||
version = "0.100.0"
|
||||
dependencies = [
|
||||
"bitcoin-internals",
|
||||
"bitcoin-io",
|
||||
"bitcoin-units",
|
||||
"mutagen",
|
||||
"ordered",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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)'] }
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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>
|
|
@ -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");
|
|
@ -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);
|
Loading…
Reference in New Issue