Add new type for sequence
This commit is contained in:
parent
97c680db8c
commit
e34bc538c3
|
@ -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;
|
||||
|
@ -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(),
|
||||
});
|
||||
|
||||
|
@ -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());
|
||||
|
|
|
@ -27,12 +27,13 @@ use crate::prelude::*;
|
|||
|
||||
use crate::io;
|
||||
use core::{fmt, str, default::Default};
|
||||
use core::convert::TryFrom;
|
||||
|
||||
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;
|
||||
|
@ -206,7 +207,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
|
||||
|
@ -220,12 +221,212 @@ 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 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 transcation 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, Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||
pub struct Sequence(pub u32);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[non_exhaustive]
|
||||
/// An error in creating relative lock-times.
|
||||
pub enum RelativeLockTimeError {
|
||||
/// The input was too large
|
||||
IntegerOverflow(u32)
|
||||
}
|
||||
|
||||
impl Sequence {
|
||||
/// The maximum allowable sequence number.
|
||||
///
|
||||
/// This sequence number disables lock-time and replace-by-fee.
|
||||
pub const MAX: Self = Sequence(MAX_SEQUENCE);
|
||||
/// Zero value sequence.
|
||||
///
|
||||
/// This sequence number enables replace-by-fee and 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 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;
|
||||
|
||||
/// Retuns `true` if the sequence number indicates that the transaction is finalised.
|
||||
///
|
||||
/// The sequence number being equal to 0xffffffff on all txin sequences indicates
|
||||
/// that the transaction is finalised.
|
||||
#[inline]
|
||||
pub fn is_final(&self) -> bool {
|
||||
*self == Sequence::MAX
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[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 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 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)
|
||||
}
|
||||
|
||||
/// Create 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, RelativeLockTimeError> {
|
||||
if let Ok(interval) = u16::try_from(seconds / 512) {
|
||||
Ok(Sequence::from_512_second_intervals(interval))
|
||||
} else {
|
||||
Err(RelativeLockTimeError::IntegerOverflow(seconds))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create 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, RelativeLockTimeError> {
|
||||
if let Ok(interval) = u16::try_from((seconds + 511) / 512) {
|
||||
Ok(Sequence::from_512_second_intervals(interval))
|
||||
} else {
|
||||
Err(RelativeLockTimeError::IntegerOverflow(seconds))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
|
||||
#[inline]
|
||||
pub fn enables_absolute_lock_time(&self) -> bool {
|
||||
!self.is_final()
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// 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::Display for RelativeLockTimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::IntegerOverflow(val) => write!(f, "input of {} was too large", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||
impl std::error::Error for RelativeLockTimeError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::IntegerOverflow(_) => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bitcoin transaction output.
|
||||
///
|
||||
/// Defines new coins to be created as a result of the transaction,
|
||||
|
@ -481,7 +682,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(),
|
||||
});
|
||||
}
|
||||
|
@ -723,7 +924,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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,6 +966,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;
|
||||
|
@ -1088,7 +1301,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);
|
||||
}
|
||||
|
@ -1733,6 +1946,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 lock_time_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!(!lock_time_disabled.is_relative_lock_time());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "unstable"))]
|
||||
|
|
|
@ -117,6 +117,7 @@ pub use crate::blockdata::block::BlockHeader;
|
|||
pub use crate::blockdata::script::Script;
|
||||
pub use crate::blockdata::transaction::Transaction;
|
||||
pub use crate::blockdata::transaction::TxIn;
|
||||
pub use crate::blockdata::transaction::Sequence;
|
||||
pub use crate::blockdata::transaction::TxOut;
|
||||
pub use crate::blockdata::transaction::OutPoint;
|
||||
pub use crate::blockdata::transaction::EcdsaSighashType;
|
||||
|
|
|
@ -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};
|
||||
|
@ -447,7 +447,7 @@ mod tests {
|
|||
vout: 0,
|
||||
},
|
||||
script_sig: Script::new(),
|
||||
sequence: 4294967294,
|
||||
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
output: vec![
|
||||
|
@ -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![
|
||||
|
@ -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;
|
||||
|
@ -705,7 +705,7 @@ mod tests {
|
|||
vout: 0,
|
||||
},
|
||||
script_sig: Script::new(),
|
||||
sequence: 4294967294,
|
||||
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
output: vec![
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -1017,7 +1017,7 @@ mod tests {
|
|||
vout: 0,
|
||||
},
|
||||
script_sig: Script::new(),
|
||||
sequence: 4294967294,
|
||||
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
output: vec![
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue