Skip to content

Commit 90cffc0

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#1093: Add new type for sequence
e34bc53 Add new type for sequence (Noah Lanson) Pull request description: #1082 Created a new type for txin sequence field with methods to create sequences with relative time locks from block height or time units. ACKs for top commit: Kixunil: ACK e34bc53 tcharding: ACK e34bc53 apoelstra: ACK e34bc53 Tree-SHA512: 6605349d0312cc36ef9a4632f954e59265b3ba5cfd437aa88a37672fe479688aa4a3eff474902f8cc55848efe55caf3f09f321b3a62417842bfc3ec365c40688
2 parents 30acbe0 + b26b427 commit 90cffc0

File tree

4 files changed

+254
-19
lines changed

4 files changed

+254
-19
lines changed

src/blockdata/constants.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::hashes::hex::{self, HexIterator};
1616
use crate::hashes::{Hash, sha256d};
1717
use crate::blockdata::opcodes;
1818
use crate::blockdata::script;
19-
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
19+
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence};
2020
use crate::blockdata::block::{Block, BlockHeader};
2121
use crate::blockdata::witness::Witness;
2222
use crate::network::constants::Network;
@@ -83,7 +83,7 @@ fn bitcoin_genesis_tx() -> Transaction {
8383
ret.input.push(TxIn {
8484
previous_output: OutPoint::null(),
8585
script_sig: in_script,
86-
sequence: MAX_SEQUENCE,
86+
sequence: Sequence::MAX,
8787
witness: Witness::default(),
8888
});
8989

@@ -210,7 +210,7 @@ mod test {
210210
assert_eq!(serialize(&gen.input[0].script_sig),
211211
Vec::from_hex("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap());
212212

213-
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
213+
assert_eq!(gen.input[0].sequence, Sequence::MAX);
214214
assert_eq!(gen.output.len(), 1);
215215
assert_eq!(serialize(&gen.output[0].script_pubkey),
216216
Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap());

src/blockdata/transaction.rs

Lines changed: 240 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ use crate::prelude::*;
1616

1717
use crate::io;
1818
use core::{fmt, str, default::Default};
19+
use core::convert::TryFrom;
1920

2021
use crate::hashes::{self, Hash, sha256d};
2122
use crate::hashes::hex::FromHex;
2223

2324
use crate::util::endian;
24-
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
25+
use crate::blockdata::constants::{WITNESS_SCALE_FACTOR, MAX_SEQUENCE};
2526
#[cfg(feature="bitcoinconsensus")] use crate::blockdata::script;
2627
use crate::blockdata::script::Script;
2728
use crate::blockdata::witness::Witness;
@@ -195,7 +196,7 @@ pub struct TxIn {
195196
/// conflicting transactions should be preferred, or 0xFFFFFFFF
196197
/// to ignore this feature. This is generally never used since
197198
/// the miner behaviour cannot be enforced.
198-
pub sequence: u32,
199+
pub sequence: Sequence,
199200
/// Witness data: an array of byte-arrays.
200201
/// Note that this field is *not* (de)serialized with the rest of the TxIn in
201202
/// Encodable/Decodable, as it is (de)serialized at the end of the full
@@ -209,12 +210,212 @@ impl Default for TxIn {
209210
TxIn {
210211
previous_output: OutPoint::default(),
211212
script_sig: Script::new(),
212-
sequence: u32::max_value(),
213+
sequence: Sequence::MAX,
213214
witness: Witness::default(),
214215
}
215216
}
216217
}
217218

219+
/// Bitcoin transaction input sequence number.
220+
///
221+
/// The sequence field is used for:
222+
/// - Indicating whether absolute lock-time (specified in `lock_time` field of [`Transaction`])
223+
/// is enabled.
224+
/// - Indicating and encoding [BIP-68] relative lock-times.
225+
/// - Indicating whether a transcation opts-in to [BIP-125] replace-by-fee.
226+
///
227+
/// Note that transactions spending an output with `OP_CHECKLOCKTIMEVERIFY`MUST NOT use
228+
/// `Sequence::MAX` for the corresponding input. [BIP-65]
229+
///
230+
/// [BIP-65]: <https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki>
231+
/// [BIP-68]: <https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki>
232+
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki>
233+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
234+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
235+
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
236+
pub struct Sequence(pub u32);
237+
238+
#[derive(Clone, PartialEq, Eq, Debug)]
239+
#[non_exhaustive]
240+
/// An error in creating relative lock-times.
241+
pub enum RelativeLockTimeError {
242+
/// The input was too large
243+
IntegerOverflow(u32)
244+
}
245+
246+
impl Sequence {
247+
/// The maximum allowable sequence number.
248+
///
249+
/// This sequence number disables lock-time and replace-by-fee.
250+
pub const MAX: Self = Sequence(MAX_SEQUENCE);
251+
/// Zero value sequence.
252+
///
253+
/// This sequence number enables replace-by-fee and lock-time.
254+
pub const ZERO: Self = Sequence(0);
255+
/// The sequence number that enables absolute lock-time but disables replace-by-fee
256+
/// and relative lock-time.
257+
pub const ENABLE_LOCKTIME_NO_RBF: Self = Sequence::MIN_NO_RBF;
258+
/// The sequence number that enables replace-by-fee and absolute lock-time but
259+
/// disables relative lock-time.
260+
pub const ENABLE_RBF_NO_LOCKTIME: Self = Sequence(0xFFFFFFFD);
261+
262+
/// The lowest sequence number that does not opt-in for replace-by-fee.
263+
///
264+
/// A transaction is considered to have opted in to replacement of itself
265+
/// if any of it's inputs have a `Sequence` number less than this value
266+
/// (Explicit Signalling [BIP-125]).
267+
///
268+
/// [BIP-125]: <https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki]>
269+
const MIN_NO_RBF: Self = Sequence(0xFFFFFFFE);
270+
/// BIP-68 relative lock-time disable flag mask
271+
const LOCK_TIME_DISABLE_FLAG_MASK: u32 = 0x80000000;
272+
/// BIP-68 relative lock-time type flag mask
273+
const LOCK_TYPE_MASK: u32 = 0x00400000;
274+
275+
/// Retuns `true` if the sequence number indicates that the transaction is finalised.
276+
///
277+
/// The sequence number being equal to 0xffffffff on all txin sequences indicates
278+
/// that the transaction is finalised.
279+
#[inline]
280+
pub fn is_final(&self) -> bool {
281+
*self == Sequence::MAX
282+
}
283+
284+
/// Returns true if the transaction opted-in to BIP125 replace-by-fee.
285+
///
286+
/// Replace by fee is signaled by the sequence being less than 0xfffffffe which is checked by this method.
287+
#[inline]
288+
pub fn is_rbf(&self) -> bool {
289+
*self < Sequence::MIN_NO_RBF
290+
}
291+
292+
/// Returns `true` if the sequence has a relative lock-time.
293+
#[inline]
294+
pub fn is_relative_lock_time(&self) -> bool {
295+
self.0 & Sequence::LOCK_TIME_DISABLE_FLAG_MASK == 0
296+
}
297+
298+
/// Returns `true` if the sequence number encodes a block based relative lock-time.
299+
#[inline]
300+
pub fn is_height_locked(&self) -> bool {
301+
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK == 0)
302+
}
303+
304+
/// Returns `true` if the sequene number encodes a time interval based relative lock-time.
305+
#[inline]
306+
pub fn is_time_locked(&self) -> bool {
307+
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
308+
}
309+
310+
/// Create a relative lock-time using block height.
311+
#[inline]
312+
pub fn from_height(height: u16) -> Self {
313+
Sequence(u32::from(height))
314+
}
315+
316+
/// Create a relative lock-time using time intervals where each interval is equivalent
317+
/// to 512 seconds.
318+
///
319+
/// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin
320+
#[inline]
321+
pub fn from_512_second_intervals(intervals: u16) -> Self {
322+
Sequence(u32::from(intervals) | Sequence::LOCK_TYPE_MASK)
323+
}
324+
325+
/// Create a relative lock-time from seconds, converting the seconds into 512 second
326+
/// interval with floor division.
327+
///
328+
/// Will return an error if the input cannot be encoded in 16 bits.
329+
#[inline]
330+
pub fn from_seconds_floor(seconds: u32) -> Result<Self, RelativeLockTimeError> {
331+
if let Ok(interval) = u16::try_from(seconds / 512) {
332+
Ok(Sequence::from_512_second_intervals(interval))
333+
} else {
334+
Err(RelativeLockTimeError::IntegerOverflow(seconds))
335+
}
336+
}
337+
338+
/// Create a relative lock-time from seconds, converting the seconds into 512 second
339+
/// interval with ceiling division.
340+
///
341+
/// Will return an error if the input cannot be encoded in 16 bits.
342+
#[inline]
343+
pub fn from_seconds_ceil(seconds: u32) -> Result<Self, RelativeLockTimeError> {
344+
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
345+
Ok(Sequence::from_512_second_intervals(interval))
346+
} else {
347+
Err(RelativeLockTimeError::IntegerOverflow(seconds))
348+
}
349+
}
350+
351+
/// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
352+
#[inline]
353+
pub fn enables_absolute_lock_time(&self) -> bool {
354+
!self.is_final()
355+
}
356+
357+
/// Create a sequence from a u32 value.
358+
#[inline]
359+
pub fn from_consensus(n: u32) -> Self {
360+
Sequence(n)
361+
}
362+
363+
/// Returns the inner 32bit integer value of Sequence.
364+
#[inline]
365+
pub fn to_consensus_u32(&self) -> u32 {
366+
self.0
367+
}
368+
}
369+
370+
impl Default for Sequence {
371+
/// The default value of sequence is 0xffffffff.
372+
fn default() -> Self {
373+
Sequence::MAX
374+
}
375+
}
376+
377+
impl From<Sequence> for u32 {
378+
fn from(sequence: Sequence) -> u32 {
379+
sequence.0
380+
}
381+
}
382+
383+
impl fmt::Display for Sequence {
384+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385+
fmt::Display::fmt(&self.0, f)
386+
}
387+
}
388+
389+
impl fmt::LowerHex for Sequence {
390+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391+
fmt::LowerHex::fmt(&self.0, f)
392+
}
393+
}
394+
395+
impl fmt::UpperHex for Sequence {
396+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397+
fmt::UpperHex::fmt(&self.0, f)
398+
}
399+
}
400+
401+
impl fmt::Display for RelativeLockTimeError {
402+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403+
match *self {
404+
Self::IntegerOverflow(val) => write!(f, "input of {} was too large", val)
405+
}
406+
}
407+
}
408+
409+
#[cfg(feature = "std")]
410+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
411+
impl std::error::Error for RelativeLockTimeError {
412+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
413+
match self {
414+
Self::IntegerOverflow(_) => None
415+
}
416+
}
417+
}
418+
218419
/// Bitcoin transaction output.
219420
///
220421
/// Defines new coins to be created as a result of the transaction,
@@ -470,7 +671,7 @@ impl Transaction {
470671
tx.input.push(TxIn {
471672
previous_output: input.previous_output,
472673
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
473-
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { 0 } else { input.sequence },
674+
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { Sequence::ZERO } else { input.sequence },
474675
witness: Witness::default(),
475676
});
476677
}
@@ -712,7 +913,7 @@ impl Transaction {
712913
/// **does not** cover the case where a transaction becomes replaceable due to ancestors being
713914
/// RBF.
714915
pub fn is_explicitly_rbf(&self) -> bool {
715-
self.input.iter().any(|input| input.sequence < (0xffffffff - 1))
916+
self.input.iter().any(|input| input.sequence.is_rbf())
716917
}
717918
}
718919

@@ -754,6 +955,18 @@ impl Decodable for TxIn {
754955
}
755956
}
756957

958+
impl Encodable for Sequence {
959+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
960+
self.0.consensus_encode(w)
961+
}
962+
}
963+
964+
impl Decodable for Sequence {
965+
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
966+
Decodable::consensus_decode(r).map(Sequence)
967+
}
968+
}
969+
757970
impl Encodable for Transaction {
758971
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
759972
let mut len = 0;
@@ -1077,7 +1290,7 @@ mod tests {
10771290
let txin = TxIn::default();
10781291
assert_eq!(txin.previous_output, OutPoint::default());
10791292
assert_eq!(txin.script_sig, Script::new());
1080-
assert_eq!(txin.sequence, 0xFFFFFFFF);
1293+
assert_eq!(txin.sequence, Sequence::from_consensus(0xFFFFFFFF));
10811294
assert_eq!(txin.previous_output, OutPoint::default());
10821295
assert_eq!(txin.witness.len(), 0);
10831296
}
@@ -1722,6 +1935,27 @@ mod tests {
17221935
_ => panic!("Wrong error type"),
17231936
}
17241937
}
1938+
1939+
#[test]
1940+
fn sequence_number_tests() {
1941+
let seq_final = Sequence::from_consensus(0xFFFFFFFF);
1942+
let seq_non_rbf = Sequence::from_consensus(0xFFFFFFFE);
1943+
let block_time_lock = Sequence::from_consensus(0xFFFF);
1944+
let unit_time_lock = Sequence::from_consensus(0x40FFFF);
1945+
let lock_time_disabled = Sequence::from_consensus(0x80000000);
1946+
1947+
assert!(seq_final.is_final());
1948+
assert!(!seq_final.is_rbf());
1949+
assert!(!seq_final.is_relative_lock_time());
1950+
assert!(!seq_non_rbf.is_rbf());
1951+
assert!(block_time_lock.is_relative_lock_time());
1952+
assert!(block_time_lock.is_height_locked());
1953+
assert!(block_time_lock.is_rbf());
1954+
assert!(unit_time_lock.is_relative_lock_time());
1955+
assert!(unit_time_lock.is_time_locked());
1956+
assert!(unit_time_lock.is_rbf());
1957+
assert!(!lock_time_disabled.is_relative_lock_time());
1958+
}
17251959
}
17261960

17271961
#[cfg(bench)]

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub use crate::blockdata::block::BlockHeader;
103103
pub use crate::blockdata::script::Script;
104104
pub use crate::blockdata::transaction::Transaction;
105105
pub use crate::blockdata::transaction::TxIn;
106+
pub use crate::blockdata::transaction::Sequence;
106107
pub use crate::blockdata::transaction::TxOut;
107108
pub use crate::blockdata::transaction::OutPoint;
108109
pub use crate::blockdata::transaction::EcdsaSighashType;

0 commit comments

Comments
 (0)