Skip to content

Commit

Permalink
Add new type for sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
nlanson committed Jul 12, 2022
1 parent 97c680d commit 9083777
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 19 deletions.
6 changes: 3 additions & 3 deletions src/blockdata/constants.rs
Expand Up @@ -27,7 +27,7 @@ use crate::hashes::hex::{self, HexIterator};
use crate::hashes::sha256d;
use crate::blockdata::opcodes;
use crate::blockdata::script;
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence};
use crate::blockdata::block::{Block, BlockHeader};
use crate::blockdata::witness::Witness;
use crate::network::constants::Network;
Expand Down Expand Up @@ -94,7 +94,7 @@ fn bitcoin_genesis_tx() -> Transaction {
ret.input.push(TxIn {
previous_output: OutPoint::null(),
script_sig: in_script,
sequence: MAX_SEQUENCE,
sequence: Sequence::MAX,
witness: Witness::default(),
});

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

assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
assert_eq!(gen.input[0].sequence, Sequence::MAX);
assert_eq!(gen.output.len(), 1);
assert_eq!(serialize(&gen.output[0].script_pubkey),
Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap());
Expand Down
131 changes: 125 additions & 6 deletions src/blockdata/transaction.rs
Expand Up @@ -32,7 +32,7 @@ use crate::hashes::{self, Hash, sha256d};
use crate::hashes::hex::FromHex;

use crate::util::endian;
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
use crate::blockdata::constants::{WITNESS_SCALE_FACTOR, MAX_SEQUENCE};
#[cfg(feature="bitcoinconsensus")] use crate::blockdata::script;
use crate::blockdata::script::Script;
use crate::blockdata::witness::Witness;
Expand Down Expand Up @@ -206,7 +206,7 @@ pub struct TxIn {
/// conflicting transactions should be preferred, or 0xFFFFFFFF
/// to ignore this feature. This is generally never used since
/// the miner behaviour cannot be enforced.
pub sequence: u32,
pub sequence: Sequence,
/// Witness data: an array of byte-arrays.
/// Note that this field is *not* (de)serialized with the rest of the TxIn in
/// Encodable/Decodable, as it is (de)serialized at the end of the full
Expand All @@ -220,12 +220,98 @@ impl Default for TxIn {
TxIn {
previous_output: OutPoint::default(),
script_sig: Script::new(),
sequence: u32::max_value(),
sequence: Sequence::MAX,
witness: Witness::default(),
}
}
}

/// Bitcoin transaction input sequencing number.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Sequence(pub u32);

impl Sequence {
/// The maximum allowable sequence number
pub const MAX: Self = Sequence(MAX_SEQUENCE);
/// The lowest sequence number that does not opt-in for replace-by-fee.
pub const RBF_SIGNAL: Self = Sequence(0xFFFFFFFE);
/// Zero value sequence
pub const ZERO: Self = Sequence(0);
/// BIP-68 relative time lock disable flag mask
const DISABLE_FLAG_MASK: u32 = 0x80000000;
/// BIP-68 relative time lock type flag mask
const LOCK_TYPE_MASK: u32 = 0x00400000;

/// Retuns `true` if the sequence number is 0xffffffff.
#[inline]
pub fn is_final(&self) -> bool {
*self == Sequence::MAX
}

/// Returns `true` if the sequence number is less than 0xfffffffe which
/// indicates that the transaction is opted-in to BIP125 replace-by-fee.
#[inline]
pub fn is_rbf(&self) -> bool {
*self < Sequence::RBF_SIGNAL
}

/// Returns `true` if the sequence has a relative locktime.
#[inline]
pub fn is_relative_lock_time(&self) -> bool {
self.0 & Sequence::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 sequene 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)
}

/// Create a relative lock-time using block height.
#[inline]
pub fn from_height(height: u16) -> Self {
Sequence(u32::from(height))
}

/// Create a relative lock-time using time units where each unit is equivalent to 512 seconds.
#[inline]
pub fn from_timeunits(units: u16) -> Self {
Sequence(u32::from(units) | Sequence::LOCK_TYPE_MASK)
}

/// Create 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
}
}

impl Default for Sequence {
fn default() -> Self {
Sequence::MAX
}
}

impl From<Sequence> for u32 {
fn from(sequence: Sequence) -> u32 {
sequence.0
}
}

/// Bitcoin transaction output.
///
/// Defines new coins to be created as a result of the transaction,
Expand Down Expand Up @@ -481,7 +567,7 @@ impl Transaction {
tx.input.push(TxIn {
previous_output: input.previous_output,
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { 0 } else { input.sequence },
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { Sequence::ZERO } else { input.sequence },
witness: Witness::default(),
});
}
Expand Down Expand Up @@ -723,7 +809,7 @@ impl Transaction {
/// **does not** cover the case where a transaction becomes replaceable due to ancestors being
/// RBF.
pub fn is_explicitly_rbf(&self) -> bool {
self.input.iter().any(|input| input.sequence < (0xffffffff - 1))
self.input.iter().any(|input| input.sequence.is_rbf())
}
}

Expand Down Expand Up @@ -765,6 +851,18 @@ impl Decodable for TxIn {
}
}

impl Encodable for Sequence {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}

impl Decodable for Sequence {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Decodable::consensus_decode(r).map(Sequence)
}
}

impl Encodable for Transaction {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let mut len = 0;
Expand Down Expand Up @@ -1088,7 +1186,7 @@ mod tests {
let txin = TxIn::default();
assert_eq!(txin.previous_output, OutPoint::default());
assert_eq!(txin.script_sig, Script::new());
assert_eq!(txin.sequence, 0xFFFFFFFF);
assert_eq!(txin.sequence, Sequence::from_consensus(0xFFFFFFFF));
assert_eq!(txin.previous_output, OutPoint::default());
assert_eq!(txin.witness.len(), 0);
}
Expand Down Expand Up @@ -1733,6 +1831,27 @@ mod tests {
_ => panic!("Wrong error type"),
}
}

#[test]
fn sequence_number_tests() {
let seq_final = Sequence::from_consensus(0xFFFFFFFF);
let seq_non_rbf = Sequence::from_consensus(0xFFFFFFFE);
let block_time_lock = Sequence::from_consensus(0xFFFF);
let unit_time_lock = Sequence::from_consensus(0x40FFFF);
let locktime_disabled = Sequence::from_consensus(0x80000000);

assert!(seq_final.is_final());
assert!(!seq_final.is_rbf());
assert!(!seq_final.is_relative_lock_time());
assert!(!seq_non_rbf.is_rbf());
assert!(block_time_lock.is_relative_lock_time());
assert!(block_time_lock.is_height_locked());
assert!(block_time_lock.is_rbf());
assert!(unit_time_lock.is_relative_lock_time());
assert!(unit_time_lock.is_time_locked());
assert!(unit_time_lock.is_rbf());
assert!(!locktime_disabled.is_relative_lock_time());
}
}

#[cfg(all(test, feature = "unstable"))]
Expand Down
20 changes: 10 additions & 10 deletions src/util/psbt/mod.rs
Expand Up @@ -353,7 +353,7 @@ mod tests {
use secp256k1::{Secp256k1, self};

use crate::blockdata::script::Script;
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint};
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
use crate::network::constants::Network::Bitcoin;
use crate::consensus::encode::{deserialize, serialize, serialize_hex};
use crate::util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
Expand Down Expand Up @@ -447,7 +447,7 @@ mod tests {
vout: 0,
},
script_sig: Script::new(),
sequence: 4294967294,
sequence: Sequence::RBF_SIGNAL,
witness: Witness::default(),
}],
output: vec![
Expand Down Expand Up @@ -518,7 +518,7 @@ mod tests {
vout: 1,
},
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
sequence: 4294967295,
sequence: Sequence::MAX,
witness: Witness::from_vec(vec![Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap()]),
}],
output: vec![
Expand Down Expand Up @@ -607,7 +607,7 @@ mod tests {
use crate::hash_types::Txid;

use crate::blockdata::script::Script;
use crate::blockdata::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, OutPoint};
use crate::blockdata::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, OutPoint, Sequence};
use crate::consensus::encode::serialize_hex;
use crate::util::psbt::map::{Map, Input, Output};
use crate::util::psbt::raw;
Expand Down Expand Up @@ -705,7 +705,7 @@ mod tests {
vout: 0,
},
script_sig: Script::new(),
sequence: 4294967294,
sequence: Sequence::RBF_SIGNAL,
witness: Witness::default(),
}],
output: vec![
Expand Down Expand Up @@ -736,7 +736,7 @@ mod tests {
vout: 1,
},
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
sequence: 4294967295,
sequence: Sequence::MAX,
witness: Witness::from_vec(vec![
Vec::from_hex("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").unwrap(),
Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap(),
Expand All @@ -750,7 +750,7 @@ mod tests {
vout: 1,
},
script_sig: hex_script!("160014fe3e9ef1a745e974d902c4355943abcb34bd5353"),
sequence: 4294967295,
sequence: Sequence::MAX,
witness: Witness::from_vec(vec![
Vec::from_hex("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").unwrap(),
Vec::from_hex("0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3").unwrap(),
Expand Down Expand Up @@ -1017,7 +1017,7 @@ mod tests {
vout: 0,
},
script_sig: Script::new(),
sequence: 4294967294,
sequence: Sequence::RBF_SIGNAL,
witness: Witness::default(),
}],
output: vec![
Expand Down Expand Up @@ -1048,7 +1048,7 @@ mod tests {
vout: 1,
},
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
sequence: 4294967295,
sequence: Sequence::MAX,
witness: Witness::from_vec(vec![
Vec::from_hex("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").unwrap(),
Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap(),
Expand All @@ -1062,7 +1062,7 @@ mod tests {
vout: 1,
},
script_sig: hex_script!("160014fe3e9ef1a745e974d902c4355943abcb34bd5353"),
sequence: 4294967295,
sequence: Sequence::MAX,
witness: Witness::from_vec(vec![
Vec::from_hex("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").unwrap(),
Vec::from_hex("0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3").unwrap(),
Expand Down

0 comments on commit 9083777

Please sign in to comment.