Skip to content

Commit b26b427

Browse files
committed
Add new type for sequence
1 parent 07692bb commit b26b427

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
@@ -27,7 +27,7 @@ use crate::hashes::hex::{self, HexIterator};
2727
use crate::hashes::sha256d;
2828
use crate::blockdata::opcodes;
2929
use crate::blockdata::script;
30-
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
30+
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence};
3131
use crate::blockdata::block::{Block, BlockHeader};
3232
use crate::blockdata::witness::Witness;
3333
use crate::network::constants::Network;
@@ -94,7 +94,7 @@ fn bitcoin_genesis_tx() -> Transaction {
9494
ret.input.push(TxIn {
9595
previous_output: OutPoint::null(),
9696
script_sig: in_script,
97-
sequence: MAX_SEQUENCE,
97+
sequence: Sequence::MAX,
9898
witness: Witness::default(),
9999
});
100100

@@ -222,7 +222,7 @@ mod test {
222222
assert_eq!(serialize(&gen.input[0].script_sig),
223223
Vec::from_hex("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap());
224224

225-
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
225+
assert_eq!(gen.input[0].sequence, Sequence::MAX);
226226
assert_eq!(gen.output.len(), 1);
227227
assert_eq!(serialize(&gen.output[0].script_pubkey),
228228
Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap());

src/blockdata/transaction.rs

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

2828
use crate::io;
2929
use core::{fmt, str, default::Default};
30+
use core::convert::TryFrom;
3031

3132
use crate::hashes::{self, Hash, sha256d};
3233
use crate::hashes::hex::FromHex;
3334

3435
use crate::util::endian;
35-
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
36+
use crate::blockdata::constants::{WITNESS_SCALE_FACTOR, MAX_SEQUENCE};
3637
#[cfg(feature="bitcoinconsensus")] use crate::blockdata::script;
3738
use crate::blockdata::script::Script;
3839
use crate::blockdata::witness::Witness;
@@ -206,7 +207,7 @@ pub struct TxIn {
206207
/// conflicting transactions should be preferred, or 0xFFFFFFFF
207208
/// to ignore this feature. This is generally never used since
208209
/// the miner behaviour cannot be enforced.
209-
pub sequence: u32,
210+
pub sequence: Sequence,
210211
/// Witness data: an array of byte-arrays.
211212
/// Note that this field is *not* (de)serialized with the rest of the TxIn in
212213
/// Encodable/Decodable, as it is (de)serialized at the end of the full
@@ -220,12 +221,212 @@ impl Default for TxIn {
220221
TxIn {
221222
previous_output: OutPoint::default(),
222223
script_sig: Script::new(),
223-
sequence: u32::max_value(),
224+
sequence: Sequence::MAX,
224225
witness: Witness::default(),
225226
}
226227
}
227228
}
228229

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

@@ -765,6 +966,18 @@ impl Decodable for TxIn {
765966
}
766967
}
767968

969+
impl Encodable for Sequence {
970+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
971+
self.0.consensus_encode(w)
972+
}
973+
}
974+
975+
impl Decodable for Sequence {
976+
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
977+
Decodable::consensus_decode(r).map(Sequence)
978+
}
979+
}
980+
768981
impl Encodable for Transaction {
769982
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
770983
let mut len = 0;
@@ -1088,7 +1301,7 @@ mod tests {
10881301
let txin = TxIn::default();
10891302
assert_eq!(txin.previous_output, OutPoint::default());
10901303
assert_eq!(txin.script_sig, Script::new());
1091-
assert_eq!(txin.sequence, 0xFFFFFFFF);
1304+
assert_eq!(txin.sequence, Sequence::from_consensus(0xFFFFFFFF));
10921305
assert_eq!(txin.previous_output, OutPoint::default());
10931306
assert_eq!(txin.witness.len(), 0);
10941307
}
@@ -1733,6 +1946,27 @@ mod tests {
17331946
_ => panic!("Wrong error type"),
17341947
}
17351948
}
1949+
1950+
#[test]
1951+
fn sequence_number_tests() {
1952+
let seq_final = Sequence::from_consensus(0xFFFFFFFF);
1953+
let seq_non_rbf = Sequence::from_consensus(0xFFFFFFFE);
1954+
let block_time_lock = Sequence::from_consensus(0xFFFF);
1955+
let unit_time_lock = Sequence::from_consensus(0x40FFFF);
1956+
let lock_time_disabled = Sequence::from_consensus(0x80000000);
1957+
1958+
assert!(seq_final.is_final());
1959+
assert!(!seq_final.is_rbf());
1960+
assert!(!seq_final.is_relative_lock_time());
1961+
assert!(!seq_non_rbf.is_rbf());
1962+
assert!(block_time_lock.is_relative_lock_time());
1963+
assert!(block_time_lock.is_height_locked());
1964+
assert!(block_time_lock.is_rbf());
1965+
assert!(unit_time_lock.is_relative_lock_time());
1966+
assert!(unit_time_lock.is_time_locked());
1967+
assert!(unit_time_lock.is_rbf());
1968+
assert!(!lock_time_disabled.is_relative_lock_time());
1969+
}
17361970
}
17371971

17381972
#[cfg(all(test, feature = "unstable"))]

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub use crate::blockdata::block::BlockHeader;
117117
pub use crate::blockdata::script::Script;
118118
pub use crate::blockdata::transaction::Transaction;
119119
pub use crate::blockdata::transaction::TxIn;
120+
pub use crate::blockdata::transaction::Sequence;
120121
pub use crate::blockdata::transaction::TxOut;
121122
pub use crate::blockdata::transaction::OutPoint;
122123
pub use crate::blockdata::transaction::EcdsaSighashType;

0 commit comments

Comments
 (0)