// SPDX-License-Identifier: CC0-1.0

//! Bitcoin transactions.
//!
//! A transaction describes a transfer of money. It consumes previously-unspent
//! transaction outputs and produces new ones, satisfying the condition to spend
//! the old outputs (typically a digital signature with a specific key must be
//! provided) and defining the condition to spend the new ones. The use of digital
//! signatures ensures that coins cannot be spent by unauthorized parties.
//!
//! This module provides the structures and functions needed to support transactions.

#[cfg(feature = "alloc")]
use core::cmp;
#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
use core::convert::Infallible;
use core::fmt;

#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use hashes::sha256d;
#[cfg(feature = "alloc")]
use internals::compact_size;
#[cfg(feature = "hex")]
use internals::write_err;
#[cfg(feature = "hex")]
use units::parse;
#[cfg(feature = "alloc")]
use units::{Amount, Weight};

#[cfg(feature = "alloc")]
use crate::locktime::absolute;
#[cfg(feature = "alloc")]
use crate::prelude::Vec;
#[cfg(feature = "alloc")]
use crate::script::ScriptBuf;
#[cfg(feature = "alloc")]
use crate::sequence::Sequence;
#[cfg(feature = "alloc")]
use crate::witness::Witness;

/// Bitcoin transaction.
///
/// An authenticated movement of coins.
///
/// See [Bitcoin Wiki: Transaction][wiki-transaction] for more information.
///
/// [wiki-transaction]: https://en.bitcoin.it/wiki/Transaction
///
/// ### Bitcoin Core References
///
/// * [CTtransaction definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/transaction.h#L279)
///
/// ### Serialization notes
///
/// If any inputs have nonempty witnesses, the entire transaction is serialized
/// in the post-BIP141 SegWit format which includes a list of witnesses. If all
/// inputs have empty witnesses, the transaction is serialized in the pre-BIP141
/// format.
///
/// There is one major exception to this: to avoid deserialization ambiguity,
/// if the transaction has no inputs, it is serialized in the BIP141 style. Be
/// aware that this differs from the transaction format in PSBT, which _never_
/// uses BIP141. (Ordinarily there is no conflict, since in PSBT transactions
/// are always unsigned and therefore their inputs have empty witnesses.)
///
/// The specific ambiguity is that SegWit uses the flag bytes `0001` where an old
/// serializer would read the number of transaction inputs. The old serializer
/// would interpret this as "no inputs, one output", which means the transaction
/// is invalid, and simply reject it. SegWit further specifies that this encoding
/// should *only* be used when some input has a nonempty witness; that is,
/// witness-less transactions should be encoded in the traditional format.
///
/// However, in protocols where transactions may legitimately have 0 inputs, e.g.
/// when parties are cooperatively funding a transaction, the "00 means SegWit"
/// heuristic does not work. Since SegWit requires such a transaction be encoded
/// in the original transaction format (since it has no inputs and therefore
/// no input witnesses), a traditionally encoded transaction may have the `0001`
/// SegWit flag in it, which confuses most SegWit parsers including the one in
/// Bitcoin Core.
///
/// We therefore deviate from the spec by always using the SegWit witness encoding
/// for 0-input transactions, which results in unambiguously parseable transactions.
///
/// ### A note on ordering
///
/// This type implements `Ord`, even though it contains a locktime, which is not
/// itself `Ord`. This was done to simplify applications that may need to hold
/// transactions inside a sorted container. We have ordered the locktimes based
/// on their representation as a `u32`, which is not a semantically meaningful
/// order, and therefore the ordering on `Transaction` itself is not semantically
/// meaningful either.
///
/// The ordering is, however, consistent with the ordering present in this library
/// before this change, so users should not notice any breakage (here) when
/// transitioning from 0.29 to 0.30.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg(feature = "alloc")]
pub struct Transaction {
    /// The protocol version, is currently expected to be 1, 2 (BIP 68) or 3 (BIP 431).
    pub version: Version,
    /// Block height or timestamp. Transaction cannot be included in a block until this height/time.
    ///
    /// ### Relevant BIPs
    ///
    /// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
    /// * [BIP-113 Median time-past as endpoint for lock-time calculations](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki)
    pub lock_time: absolute::LockTime,
    /// List of transaction inputs.
    pub input: Vec<TxIn>,
    /// List of transaction outputs.
    pub output: Vec<TxOut>,
}

#[cfg(feature = "alloc")]
impl Transaction {
    // https://github.com/bitcoin/bitcoin/blob/44b05bf3fef2468783dcebf651654fdd30717e7e/src/policy/policy.h#L27
    /// Maximum transaction weight for Bitcoin Core 25.0.
    pub const MAX_STANDARD_WEIGHT: Weight = Weight::from_wu(400_000);

    /// Returns a reference to the transaction inputs.
    #[inline]
    pub fn inputs(&self) -> &[TxIn] { &self.input }

    /// Returns a mutable reference to the transaction inputs.
    #[inline]
    pub fn inputs_mut(&mut self) -> &mut [TxIn] { &mut self.input }

    /// Returns a reference to the transaction outputs.
    #[inline]
    pub fn outputs(&self) -> &[TxOut] { &self.output }

    /// Returns a mutable reference to the transaction outputs.
    #[inline]
    pub fn outputs_mut(&mut self) -> &mut [TxOut] { &mut self.output }

    /// Computes a "normalized TXID" which does not include any signatures.
    ///
    /// This gives a way to identify a transaction that is "the same" as
    /// another in the sense of having same inputs and outputs.
    #[doc(alias = "ntxid")]
    pub fn compute_ntxid(&self) -> sha256d::Hash {
        let cloned_tx = Transaction {
            version: self.version,
            lock_time: self.lock_time,
            input: self
                .input
                .iter()
                .map(|txin| TxIn {
                    script_sig: ScriptBuf::new(),
                    witness: Witness::default(),
                    ..*txin
                })
                .collect(),
            output: self.output.clone(),
        };
        sha256d::Hash::from_byte_array(cloned_tx.compute_txid().to_byte_array())
    }

    /// Computes the [`Txid`].
    ///
    /// Hashes the transaction **excluding** the SegWit data (i.e. the marker, flag bytes, and the
    /// witness fields themselves). For non-SegWit transactions which do not have any SegWit data,
    /// this will be equal to [`Transaction::compute_wtxid()`].
    #[doc(alias = "txid")]
    #[inline]
    pub fn compute_txid(&self) -> Txid {
        let hash = hash_transaction(self, false);
        Txid::from_byte_array(hash.to_byte_array())
    }

    /// Computes the SegWit version of the transaction id.
    ///
    /// Hashes the transaction **including** all SegWit data (i.e. the marker, flag bytes, and the
    /// witness fields themselves). For non-SegWit transactions which do not have any SegWit data,
    /// this will be equal to [`Transaction::compute_txid()`].
    #[doc(alias = "wtxid")]
    #[inline]
    pub fn compute_wtxid(&self) -> Wtxid {
        let hash = hash_transaction(self, self.uses_segwit_serialization());
        Wtxid::from_byte_array(hash.to_byte_array())
    }

    /// Returns whether or not to serialize transaction as specified in BIP-144.
    // This is duplicated in `bitcoin`, if you change it please do so in both places.
    #[inline]
    fn uses_segwit_serialization(&self) -> bool {
        if self.input.iter().any(|input| !input.witness.is_empty()) {
            return true;
        }
        // To avoid serialization ambiguity, no inputs means we use BIP141 serialization (see
        // `Transaction` docs for full explanation).
        self.input.is_empty()
    }
}

#[cfg(feature = "alloc")]
impl cmp::PartialOrd for Transaction {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
}

#[cfg(feature = "alloc")]
impl cmp::Ord for Transaction {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.version
            .cmp(&other.version)
            .then(self.lock_time.to_consensus_u32().cmp(&other.lock_time.to_consensus_u32()))
            .then(self.input.cmp(&other.input))
            .then(self.output.cmp(&other.output))
    }
}

#[cfg(feature = "alloc")]
impl From<Transaction> for Txid {
    #[inline]
    fn from(tx: Transaction) -> Txid { tx.compute_txid() }
}

#[cfg(feature = "alloc")]
impl From<&Transaction> for Txid {
    #[inline]
    fn from(tx: &Transaction) -> Txid { tx.compute_txid() }
}

#[cfg(feature = "alloc")]
impl From<Transaction> for Wtxid {
    #[inline]
    fn from(tx: Transaction) -> Wtxid { tx.compute_wtxid() }
}

#[cfg(feature = "alloc")]
impl From<&Transaction> for Wtxid {
    #[inline]
    fn from(tx: &Transaction) -> Wtxid { tx.compute_wtxid() }
}

// Duplicated in `bitcoin`.
/// The marker MUST be a 1-byte zero value: 0x00. (BIP-141)
#[cfg(feature = "alloc")]
const SEGWIT_MARKER: u8 = 0x00;
/// The flag MUST be a 1-byte non-zero value. Currently, 0x01 MUST be used. (BIP-141)
#[cfg(feature = "alloc")]
const SEGWIT_FLAG: u8 = 0x01;

// This is equivalent to consensus encoding but hashes the fields manually.
#[cfg(feature = "alloc")]
fn hash_transaction(tx: &Transaction, uses_segwit_serialization: bool) -> sha256d::Hash {
    use hashes::HashEngine as _;

    let mut enc = sha256d::Hash::engine();
    enc.input(&tx.version.0.to_le_bytes()); // Same as `encode::emit_i32`.

    if uses_segwit_serialization {
        // BIP-141 (SegWit) transaction serialization also includes marker and flag.
        enc.input(&[SEGWIT_MARKER]);
        enc.input(&[SEGWIT_FLAG]);
    }

    // Encode inputs (excluding witness data) with leading compact size encoded int.
    let input_len = tx.input.len();
    enc.input(compact_size::encode(input_len).as_slice());
    for input in &tx.input {
        // Encode each input same as we do in `Encodable for TxIn`.
        enc.input(input.previous_output.txid.as_byte_array());
        enc.input(&input.previous_output.vout.to_le_bytes());

        let script_sig_bytes = input.script_sig.as_bytes();
        enc.input(compact_size::encode(script_sig_bytes.len()).as_slice());
        enc.input(script_sig_bytes);

        enc.input(&input.sequence.0.to_le_bytes());
    }

    // Encode outputs with leading compact size encoded int.
    let output_len = tx.output.len();
    enc.input(compact_size::encode(output_len).as_slice());
    for output in &tx.output {
        // Encode each output same as we do in `Encodable for TxOut`.
        enc.input(&output.value.to_sat().to_le_bytes());

        let script_pubkey_bytes = output.script_pubkey.as_bytes();
        enc.input(compact_size::encode(script_pubkey_bytes.len()).as_slice());
        enc.input(script_pubkey_bytes);
    }

    if uses_segwit_serialization {
        // BIP-141 (SegWit) transaction serialization also includes the witness data.
        for input in &tx.input {
            // Same as `Encodable for Witness`.
            enc.input(compact_size::encode(input.witness.len()).as_slice());
            for element in &input.witness {
                enc.input(compact_size::encode(element.len()).as_slice());
                enc.input(element);
            }
        }
    }

    // Same as `Encodable for absolute::LockTime`.
    enc.input(&tx.lock_time.to_consensus_u32().to_le_bytes());

    sha256d::Hash::from_engine(enc)
}

/// Bitcoin transaction input.
///
/// It contains the location of the previous transaction's output,
/// that it spends and set of scripts that satisfy its spending
/// conditions.
///
/// ### Bitcoin Core References
///
/// * [CTxIn definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/transaction.h#L65)
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg(feature = "alloc")]
pub struct TxIn {
    /// The reference to the previous output that is being used as an input.
    pub previous_output: OutPoint,
    /// The script which pushes values on the stack which will cause
    /// the referenced output's script to be accepted.
    pub script_sig: ScriptBuf,
    /// The sequence number, which suggests to miners which of two
    /// conflicting transactions should be preferred, or 0xFFFFFFFF
    /// to ignore this feature. This is generally never used since
    /// the miner behavior cannot be enforced.
    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
    /// Transaction. It *is* (de)serialized with the rest of the `TxIn` in other
    /// (de)serialization routines.
    pub witness: Witness,
}

#[cfg(feature = "alloc")]
impl TxIn {
    /// An empty transaction input with the previous output as for a coinbase transaction.
    pub const EMPTY_COINBASE: TxIn = TxIn {
        previous_output: OutPoint::COINBASE_PREVOUT,
        script_sig: ScriptBuf::new(),
        sequence: Sequence::MAX,
        witness: Witness::new(),
    };
}

/// Bitcoin transaction output.
///
/// Defines new coins to be created as a result of the transaction,
/// along with spending conditions ("script", aka "output script"),
/// which an input spending it must satisfy.
///
/// An output that is not yet spent by an input is called Unspent Transaction Output ("UTXO").
///
/// ### Bitcoin Core References
///
/// * [CTxOut definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/transaction.h#L148)
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg(feature = "alloc")]
pub struct TxOut {
    /// The value of the output, in satoshis.
    #[cfg_attr(feature = "serde", serde(with = "crate::amount::serde::as_sat"))]
    pub value: Amount,
    /// The script which must be satisfied for the output to be spent.
    pub script_pubkey: ScriptBuf,
}

/// A reference to a transaction output.
///
/// ### Bitcoin Core References
///
/// * [COutPoint definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/transaction.h#L26)
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct OutPoint {
    /// The referenced transaction's txid.
    pub txid: Txid,
    /// The index of the referenced output in its transaction's vout.
    pub vout: u32,
}
#[cfg(feature = "serde")]
internals::serde_struct_human_string_impl!(OutPoint, "an OutPoint", txid, vout);

impl OutPoint {
    /// The number of bytes that an outpoint contributes to the size of a transaction.
    pub const SIZE: usize = 32 + 4; // The serialized lengths of txid and vout.

    /// The `OutPoint` used in a coinbase prevout.
    ///
    /// This is used as the dummy input for coinbase transactions because they don't have any
    /// previous outputs. In other words, does not point to a real transaction.
    pub const COINBASE_PREVOUT: Self = Self { txid: Txid::COINBASE_PREVOUT, vout: u32::MAX };
}

#[cfg(feature = "hex")]
impl fmt::Display for OutPoint {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.txid, self.vout)
    }
}

#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
impl core::str::FromStr for OutPoint {
    type Err = ParseOutPointError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > 75 {
            // 64 + 1 + 10
            return Err(ParseOutPointError::TooLong);
        }
        let find = s.find(':');
        if find.is_none() || find != s.rfind(':') {
            return Err(ParseOutPointError::Format);
        }
        let colon = find.unwrap();
        if colon == 0 || colon == s.len() - 1 {
            return Err(ParseOutPointError::Format);
        }
        Ok(OutPoint {
            txid: s[..colon].parse().map_err(ParseOutPointError::Txid)?,
            vout: parse_vout(&s[colon + 1..])?,
        })
    }
}

/// Parses a string-encoded transaction index (vout).
///
/// Does not permit leading zeroes or non-digit characters.
#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
fn parse_vout(s: &str) -> Result<u32, ParseOutPointError> {
    if s.len() > 1 {
        let first = s.chars().next().unwrap();
        if first == '0' || first == '+' {
            return Err(ParseOutPointError::VoutNotCanonical);
        }
    }
    parse::int_from_str(s).map_err(ParseOutPointError::Vout)
}

/// An error in parsing an [`OutPoint`].
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
pub enum ParseOutPointError {
    /// Error in TXID part.
    Txid(hex::HexToArrayError),
    /// Error in vout part.
    Vout(parse::ParseIntError),
    /// Error in general format.
    Format,
    /// Size exceeds max.
    TooLong,
    /// Vout part is not strictly numeric without leading zeroes.
    VoutNotCanonical,
}

#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
impl From<Infallible> for ParseOutPointError {
    #[inline]
    fn from(never: Infallible) -> Self { match never {} }
}

#[cfg(feature = "alloc")]
#[cfg(feature = "hex")]
impl fmt::Display for ParseOutPointError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ParseOutPointError as E;

        match *self {
            E::Txid(ref e) => write_err!(f, "error parsing TXID"; e),
            E::Vout(ref e) => write_err!(f, "error parsing vout"; e),
            E::Format => write!(f, "OutPoint not in <txid>:<vout> format"),
            E::TooLong => write!(f, "vout should be at most 10 digits"),
            E::VoutNotCanonical => write!(f, "no leading zeroes or + allowed in vout part"),
        }
    }
}

#[cfg(feature = "std")]
#[cfg(feature = "hex")]
impl std::error::Error for ParseOutPointError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use ParseOutPointError as E;

        match self {
            E::Txid(e) => Some(e),
            E::Vout(e) => Some(e),
            E::Format | E::TooLong | E::VoutNotCanonical => None,
        }
    }
}

hashes::hash_newtype! {
    /// A bitcoin transaction hash/transaction ID.
    ///
    /// For compatibility with the existing Bitcoin infrastructure and historical and current
    /// versions of the Bitcoin Core software itself, this and other [`sha256d::Hash`] types, are
    /// serialized in reverse byte order when converted to a hex string via [`std::fmt::Display`]
    /// trait operations.
    ///
    /// See [`hashes::Hash::DISPLAY_BACKWARD`] for more details.
    pub struct Txid(sha256d::Hash);

    /// A bitcoin witness transaction ID.
    pub struct Wtxid(sha256d::Hash);
}

#[cfg(feature = "hex")]
hashes::impl_hex_for_newtype!(Txid, Wtxid);
#[cfg(not(feature = "hex"))]
hashes::impl_debug_only_for_newtype!(Txid, Wtxid);
#[cfg(feature = "serde")]
hashes::impl_serde_for_newtype!(Txid, Wtxid);

impl Txid {
    /// The `Txid` used in a coinbase prevout.
    ///
    /// This is used as the "txid" of the dummy input of a coinbase transaction. This is not a real
    /// TXID and should not be used in any other contexts. See [`OutPoint::COINBASE_PREVOUT`].
    pub const COINBASE_PREVOUT: Self = Self::from_byte_array([0; 32]);
}

impl Wtxid {
    /// The `Wtxid` of a coinbase transaction.
    ///
    /// This is used as the wTXID for the coinbase transaction when constructing blocks (in the
    /// witness commitment tree) since the coinbase transaction contains a commitment to all
    /// transactions' wTXIDs but naturally cannot commit to its own.
    pub const COINBASE: Self = Self::from_byte_array([0; 32]);
}

/// The transaction version.
///
/// Currently, as specified by [BIP-68] and [BIP-431], version 1, 2, and 3 are considered standard.
///
/// Standardness of the inner `u32` 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
/// [BIP-431]: https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki
#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Version(u32);

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);

    /// The third Bitcoin transaction version (post-BIP-431).
    pub const THREE: Self = Self(3);

    /// Constructs a potentially non-standard transaction version.
    ///
    /// This can accept both standard and non-standard versions.
    #[inline]
    pub fn maybe_non_standard(version: u32) -> Version { Self(version) }

    /// Returns the inner `u32` value of this `Version`.
    #[inline]
    pub const fn to_u32(self) -> u32 { self.0 }

    /// Returns true if this transaction version number is considered standard.
    ///
    /// The behavior of this method matches whatever Bitcoin Core considers standard at the time
    /// of the release and may change in future versions to accommodate new standard versions.
    /// As of Bitcoin Core 28.0 ([release notes](https://bitcoincore.org/en/releases/28.0/)),
    /// versions 1, 2, and 3 are considered standard.
    #[inline]
    pub fn is_standard(self) -> bool {
        self == Version::ONE || self == Version::TWO || self == Version::THREE
    }
}

impl fmt::Display for Version {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}

impl From<Version> for u32 {
    #[inline]
    fn from(version: Version) -> Self { version.0 }
}

#[cfg(feature = "arbitrary")]
#[cfg(feature = "alloc")]
impl<'a> Arbitrary<'a> for Transaction {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(Transaction {
            version: Version::arbitrary(u)?,
            lock_time: absolute::LockTime::arbitrary(u)?,
            input: Vec::<TxIn>::arbitrary(u)?,
            output: Vec::<TxOut>::arbitrary(u)?,
        })
    }
}

#[cfg(feature = "arbitrary")]
#[cfg(feature = "alloc")]
impl<'a> Arbitrary<'a> for TxIn {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(TxIn {
            previous_output: OutPoint::arbitrary(u)?,
            script_sig: ScriptBuf::arbitrary(u)?,
            sequence: Sequence::arbitrary(u)?,
            witness: Witness::arbitrary(u)?,
        })
    }
}

#[cfg(feature = "arbitrary")]
#[cfg(feature = "alloc")]
impl<'a> Arbitrary<'a> for TxOut {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(TxOut { value: Amount::arbitrary(u)?, script_pubkey: ScriptBuf::arbitrary(u)? })
    }
}

#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for OutPoint {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(OutPoint { txid: Txid::arbitrary(u)?, vout: u32::arbitrary(u)? })
    }
}

#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Version {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        // Equally weight the case of normal version numbers
        let choice = u.int_in_range(0..=3)?;
        match choice {
            0 => Ok(Version::ONE),
            1 => Ok(Version::TWO),
            2 => Ok(Version::THREE),
            _ => Ok(Version(u.arbitrary()?)),
        }
    }
}

#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Txid {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        let arbitrary_bytes = u.arbitrary()?;
        let t = sha256d::Hash::from_byte_array(arbitrary_bytes);
        Ok(Txid(t))
    }
}

#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sanity_check() {
        let version = Version(123);
        assert_eq!(version.to_u32(), 123);
        assert_eq!(u32::from(version), 123);

        assert!(!version.is_standard());
        assert!(Version::ONE.is_standard());
        assert!(Version::TWO.is_standard());
        assert!(Version::THREE.is_standard());
    }

    #[test]
    fn transaction_functions() {
        let txin = TxIn {
            previous_output: OutPoint {
                txid: Txid::from_byte_array([0xAA; 32]), // Arbitrary invalid dummy value.
                vout: 0,
            },
            script_sig: ScriptBuf::new(),
            sequence: Sequence::MAX,
            witness: Witness::new(),
        };

        let txout = TxOut {
            value: Amount::from_sat(123_456_789).unwrap(),
            script_pubkey: ScriptBuf::new(),
        };

        let tx_orig = Transaction {
            version: Version::ONE,
            lock_time: absolute::LockTime::from_consensus(1_738_968_231), // The time this was written
            input: vec![txin.clone()],
            output: vec![txout.clone()],
        };

        // Test changing the transaction
        let mut tx = tx_orig.clone();
        tx.inputs_mut()[0].previous_output.txid = Txid::from_byte_array([0xFF; 32]);
        tx.outputs_mut()[0].value = Amount::from_sat(987_654_321).unwrap();
        assert_eq!(tx.inputs()[0].previous_output.txid.to_byte_array(), [0xFF; 32]);
        assert_eq!(tx.outputs()[0].value.to_sat(), 987_654_321);

        // Test uses_segwit_serialization
        assert!(!tx.uses_segwit_serialization());
        tx.input[0].witness.push(vec![0xAB, 0xCD, 0xEF]);
        assert!(tx.uses_segwit_serialization());

        // Test partial ord
        assert!(tx > tx_orig);
    }

    #[test]
    #[cfg(feature = "hex")]
    fn outpoint_from_str() {
        // Check format errors
        let mut outpoint_str = "0".repeat(64); // No ":"
        let outpoint: Result<OutPoint, ParseOutPointError> = outpoint_str.parse();
        assert_eq!(outpoint, Err(ParseOutPointError::Format));

        outpoint_str.push(':'); // Empty vout
        let outpoint: Result<OutPoint, ParseOutPointError> = outpoint_str.parse();
        assert_eq!(outpoint, Err(ParseOutPointError::Format));

        outpoint_str.push('0'); // Correct format
        let outpoint: OutPoint = outpoint_str.parse().unwrap();
        assert_eq!(outpoint.txid, Txid::from_byte_array([0; 32]));
        assert_eq!(outpoint.vout, 0);

        // Check the number of bytes OutPoint contributes to the transaction is equal to SIZE
        let outpoint_size = outpoint.txid.as_byte_array().len() + outpoint.vout.to_le_bytes().len();
        assert_eq!(outpoint_size, OutPoint::SIZE);

        // Check TooLong error
        outpoint_str.push_str("0000000000");
        let outpoint: Result<OutPoint, ParseOutPointError> = outpoint_str.parse();
        assert_eq!(outpoint, Err(ParseOutPointError::TooLong));
    }

    #[test]
    #[cfg(feature = "hex")]
    fn canonical_vout() {
        assert_eq!(parse_vout("0").unwrap(), 0);
        assert_eq!(parse_vout("1").unwrap(), 1);
        assert!(parse_vout("01").is_err()); // Leading zero not allowed
        assert!(parse_vout("+1").is_err()); // Non digits not allowed
    }
}
