Merge pull request #2006 from tcharding/08-18-tx-version

Add transaction::Version data type
This commit is contained in:
Clark Moody 2023-09-23 13:13:52 -05:00 committed by GitHub
commit 72a7280d7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 88 additions and 41 deletions

View File

@ -39,8 +39,8 @@ use bitcoin::locktime::absolute;
use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType};
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
use bitcoin::{
Address, Amount, Network, OutPoint, PublicKey, ScriptBuf, Sequence, Transaction, TxIn, TxOut,
Witness,
transaction, Address, Amount, Network, OutPoint, PublicKey, ScriptBuf, Sequence, Transaction,
TxIn, TxOut, Witness,
};
type Result<T> = std::result::Result<T, Error>;
@ -177,7 +177,7 @@ impl WatchOnly {
let change_amount = Amount::from_str(CHANGE_AMOUNT_BTC)?;
let tx = Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint { txid: INPUT_UTXO_TXID.parse()?, vout: INPUT_UTXO_VOUT },

View File

@ -88,8 +88,8 @@ use bitcoin::secp256k1::Secp256k1;
use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType};
use bitcoin::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo};
use bitcoin::{
absolute, script, Address, Amount, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut,
Witness,
absolute, script, transaction, Address, Amount, Network, OutPoint, ScriptBuf, Transaction,
TxIn, TxOut, Witness,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -229,7 +229,7 @@ fn generate_bip86_key_spend_tx(
// CREATOR + UPDATER
let tx1 = Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint { txid: input_utxo.txid.parse()?, vout: input_utxo.vout },
@ -414,7 +414,7 @@ impl BenefactorWallet {
// CREATOR + UPDATER
let next_tx = Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time,
input: vec![TxIn {
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
@ -560,7 +560,7 @@ impl BenefactorWallet {
.expect("failed to verify transaction");
let next_tx = Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time,
input: vec![TxIn {
previous_output: OutPoint { txid: tx.txid(), vout: 0 },

View File

@ -371,6 +371,7 @@ mod test {
use super::*;
use crate::blockdata::locktime::absolute;
use crate::blockdata::transaction;
use crate::consensus::encode::{deserialize, serialize};
use crate::hash_types::TxMerkleNode;
use crate::{
@ -380,7 +381,7 @@ mod test {
fn dummy_tx(nonce: &[u8]) -> Transaction {
Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::from_consensus(2),
input: vec![TxIn {
previous_output: OutPoint::new(Txid::hash(nonce), 0),

View File

@ -17,7 +17,7 @@ use crate::blockdata::block::{self, Block};
use crate::blockdata::locktime::absolute;
use crate::blockdata::opcodes::all::*;
use crate::blockdata::script;
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::witness::Witness;
use crate::internal_macros::impl_bytes_newtype;
use crate::network::Network;
@ -64,7 +64,7 @@ pub const COINBASE_MATURITY: u32 = 100;
fn bitcoin_genesis_tx() -> Transaction {
// Base
let mut ret = Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![],
output: vec![],
@ -196,6 +196,7 @@ mod test {
use super::*;
use crate::blockdata::locktime::absolute;
use crate::blockdata::transaction;
use crate::consensus::encode::serialize;
use crate::internal_macros::hex;
use crate::network::Network;
@ -204,7 +205,7 @@ mod test {
fn bitcoin_genesis_first_transaction() {
let gen = bitcoin_genesis_tx();
assert_eq!(gen.version, 1);
assert_eq!(gen.version, transaction::Version::ONE);
assert_eq!(gen.input.len(), 1);
assert_eq!(gen.input[0].previous_output.txid, Hash::all_zeros());
assert_eq!(gen.input[0].previous_output.vout, 0xFFFFFFFF);

View File

@ -96,9 +96,9 @@ impl FeeRate {
/// # Examples
///
/// ```no_run
/// # use bitcoin::{absolute, FeeRate, Transaction};
/// # use bitcoin::{absolute, transaction, FeeRate, Transaction};
/// # // Dummy transaction.
/// # let tx = Transaction { version: 1, lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![] };
/// # let tx = Transaction { version: transaction::Version::ONE, lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![] };
///
/// let rate = FeeRate::from_sat_per_vb(1).expect("1 sat/vbyte is valid");
/// let fee = rate.fee_wu(tx.weight());

View File

@ -586,7 +586,7 @@ impl TxOut {
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Transaction {
/// The protocol version, is currently expected to be 1 or 2 (BIP 68).
pub version: i32,
pub version: Version,
/// Block height or timestamp. Transaction cannot be included in a block until this height/time.
///
/// ### Relevant BIPs
@ -952,6 +952,50 @@ impl Transaction {
}
}
/// The transaction version.
///
/// Currently, as specified by [BIP-68], only version 1 and 2 are considered standard.
///
/// Standardness of the inner `i32` is not an invariant because you are free to create transactions
/// of any version, transactions with non-standard version numbers will not be relayed by the
/// Bitcoin network.
///
/// [BIP-68]: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Version(pub i32);
impl Version {
/// The original Bitcoin transaction version (pre-BIP-68).
pub const ONE: Self = Self(1);
/// The second Bitcoin transaction version (post-BIP-68).
pub const TWO: Self = Self(2);
/// Creates a non-standard transaction version.
pub fn non_standard(version: i32) -> Version { Self(version) }
/// Returns true if this transaction version number is considered standard.
pub fn is_standard(&self) -> bool { *self == Version::ONE || *self == Version::TWO }
}
impl Default for Version {
fn default() -> Version { Version::TWO }
}
impl Encodable for Version {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}
impl Decodable for Version {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Decodable::consensus_decode(r).map(Version)
}
}
impl_consensus_encoding!(TxOut, value, script_pubkey);
impl Encodable for OutPoint {
@ -1038,7 +1082,7 @@ impl Decodable for Transaction {
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
r: &mut R,
) -> Result<Self, encode::Error> {
let version = i32::consensus_decode_from_finite_reader(r)?;
let version = Version::consensus_decode_from_finite_reader(r)?;
let input = Vec::<TxIn>::consensus_decode_from_finite_reader(r)?;
// segwit
if input.is_empty() {
@ -1491,7 +1535,7 @@ mod tests {
let realtx = tx.unwrap();
// All these tests aren't really needed because if they fail, the hash check at the end
// will also fail. But these will show you where the failure is so I'll leave them in.
assert_eq!(realtx.version, 1);
assert_eq!(realtx.version, Version::ONE);
assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
// as little-endian 256-bit numbers rather than as data strings.
@ -1532,7 +1576,7 @@ mod tests {
let realtx = tx.unwrap();
// All these tests aren't really needed because if they fail, the hash check at the end
// will also fail. But these will show you where the failure is so I'll leave them in.
assert_eq!(realtx.version, 2);
assert_eq!(realtx.version, Version::TWO);
assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
// as little-endian 256-bit numbers rather than as data strings.
@ -1604,13 +1648,13 @@ mod tests {
let tx: Result<Transaction, _> = deserialize(&tx_bytes);
assert!(tx.is_ok());
let realtx = tx.unwrap();
assert_eq!(realtx.version, 2147483647);
assert_eq!(realtx.version, Version::non_standard(2147483647));
let tx2_bytes = hex!("000000800100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000");
let tx2: Result<Transaction, _> = deserialize(&tx2_bytes);
assert!(tx2.is_ok());
let realtx2 = tx2.unwrap();
assert_eq!(realtx2.version, -2147483648);
assert_eq!(realtx2.version, Version::non_standard(-2147483648));
}
#[test]
@ -1898,7 +1942,7 @@ mod tests {
];
let empty_transaction_weight = Transaction {
version: 0,
version: Version::default(),
lock_time: absolute::LockTime::ZERO,
input: vec![],
output: vec![],

View File

@ -1136,10 +1136,10 @@ impl<R: BorrowMut<Transaction>> SighashCache<R> {
///
/// This allows in-line signing such as
/// ```
/// use bitcoin::{absolute, Amount, Transaction, Script};
/// use bitcoin::{absolute, transaction, Amount, Transaction, Script};
/// use bitcoin::sighash::{EcdsaSighashType, SighashCache};
///
/// let mut tx_to_sign = Transaction { version: 2, lock_time: absolute::LockTime::ZERO, input: Vec::new(), output: Vec::new() };
/// let mut tx_to_sign = Transaction { version: transaction::Version::TWO, lock_time: absolute::LockTime::ZERO, input: Vec::new(), output: Vec::new() };
/// let input_count = tx_to_sign.input.len();
///
/// let mut sig_hasher = SighashCache::new(&mut tx_to_sign);
@ -1262,6 +1262,7 @@ mod tests {
use super::*;
use crate::blockdata::locktime::absolute;
use crate::blockdata::transaction;
use crate::consensus::deserialize;
use crate::crypto::sighash::{LegacySighash, TapSighash};
use crate::internal_macros::hex;
@ -1275,7 +1276,7 @@ mod tests {
// We need a tx with more inputs than outputs.
let tx = Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn::default(), TxIn::default()],
output: vec![TxOut::NULL],
@ -1462,7 +1463,7 @@ mod tests {
#[rustfmt::skip] // Allow long function call `taproot_signature_hash`.
fn test_sighash_errors() {
let dumb_tx = Transaction {
version: 0,
version: transaction::Version::default(),
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn::default()],
output: vec![],

View File

@ -887,7 +887,7 @@ mod tests {
use crate::bip32::{ChildNumber, KeySource, Xpriv, Xpub};
use crate::blockdata::locktime::absolute;
use crate::blockdata::script::ScriptBuf;
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
use crate::blockdata::witness::Witness;
use crate::internal_macros::hex;
use crate::network::Network::Bitcoin;
@ -899,7 +899,7 @@ mod tests {
fn trivial_psbt() {
let psbt = Psbt {
unsigned_tx: Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![],
output: vec![],
@ -972,7 +972,7 @@ mod tests {
fn serialize_then_deserialize_global() {
let expected = Psbt {
unsigned_tx: Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::from_consensus(1257139),
input: vec![TxIn {
previous_output: OutPoint {
@ -1043,7 +1043,7 @@ mod tests {
// create some values to use in the PSBT
let tx = Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint {
@ -1240,7 +1240,7 @@ mod tests {
fn valid_vector_1() {
let unserialized = Psbt {
unsigned_tx: Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::from_consensus(1257139),
input: vec![
TxIn {
@ -1272,7 +1272,7 @@ mod tests {
inputs: vec![
Input {
non_witness_utxo: Some(Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![
TxIn {
@ -1572,7 +1572,7 @@ mod tests {
// same vector as valid_vector_1 from BIPs with added
let mut unserialized = Psbt {
unsigned_tx: Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::from_consensus(1257139),
input: vec![
TxIn {
@ -1604,7 +1604,7 @@ mod tests {
inputs: vec![
Input {
non_witness_utxo: Some(Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![
TxIn {
@ -1741,7 +1741,7 @@ mod tests {
let mut t = Psbt {
unsigned_tx: Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::from_consensus(1257139),
input: vec![
TxIn {
@ -1772,7 +1772,7 @@ mod tests {
inputs: vec![
Input {
non_witness_utxo: Some(Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![
TxIn {
@ -1850,7 +1850,7 @@ mod tests {
use crate::{WPubkeyHash, WitnessProgram};
let unsigned_tx = Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn::default(), TxIn::default()],
output: vec![TxOut::NULL],

View File

@ -7,7 +7,7 @@ use std::str::FromStr;
use bitcoin::bip32::{Fingerprint, IntoDerivationPath, KeySource, Xpriv, Xpub};
use bitcoin::blockdata::opcodes::OP_0;
use bitcoin::blockdata::script;
use bitcoin::blockdata::{script, transaction};
use bitcoin::consensus::encode::{deserialize, serialize_hex};
use bitcoin::hex::FromHex;
use bitcoin::psbt::{Psbt, PsbtSighashType};
@ -163,7 +163,7 @@ fn create_transaction() -> Transaction {
}
Transaction {
version: 2,
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![
TxIn {

View File

@ -38,8 +38,8 @@ use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
use bitcoin::{
ecdsa, Address, Amount, Block, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence,
Target, Transaction, TxIn, TxOut, Txid, Work,
ecdsa, transaction, Address, Amount, Block, Network, OutPoint, PrivateKey, PublicKey,
ScriptBuf, Sequence, Target, Transaction, TxIn, TxOut, Txid, Work,
};
/// Implicitly does regression test for `BlockHeader` also.
@ -221,7 +221,7 @@ fn serde_regression_public_key() {
#[test]
fn serde_regression_psbt() {
let tx = Transaction {
version: 1,
version: transaction::Version::ONE,
lock_time: absolute::LockTime::ZERO,
input: vec![TxIn {
previous_output: OutPoint {