Skip to main content

bsv/transaction/
transaction_input.rs

1//! Bitcoin transaction input type.
2
3use std::io::{Read, Write};
4
5use crate::script::unlocking_script::UnlockingScript;
6use crate::transaction::error::TransactionError;
7use crate::transaction::transaction::Transaction;
8use crate::transaction::{read_u32_le, read_varint, write_u32_le, write_varint};
9
10/// A single input in a Bitcoin transaction.
11#[derive(Debug, Clone)]
12pub struct TransactionInput {
13    /// The full source transaction (if available, e.g. from BEEF).
14    pub source_transaction: Option<Box<Transaction>>,
15    /// The source transaction ID as hex string (display/BE format).
16    pub source_txid: Option<String>,
17    /// The index of the output being spent.
18    pub source_output_index: u32,
19    /// The unlocking script (scriptSig).
20    pub unlocking_script: Option<UnlockingScript>,
21    /// Sequence number (default 0xFFFFFFFF).
22    pub sequence: u32,
23}
24
25impl Default for TransactionInput {
26    fn default() -> Self {
27        Self {
28            source_transaction: None,
29            source_txid: None,
30            source_output_index: 0,
31            unlocking_script: None,
32            sequence: 0xFFFFFFFF,
33        }
34    }
35}
36
37impl TransactionInput {
38    /// Deserialize a transaction input from binary wire format.
39    ///
40    /// Wire format: txid(32, reversed) + output_index(4 LE) +
41    ///   varint(script_len) + script + sequence(4 LE)
42    pub fn from_binary(reader: &mut impl Read) -> Result<Self, TransactionError> {
43        // Read 32-byte txid (stored in internal/LE byte order on wire)
44        let mut txid_bytes = [0u8; 32];
45        reader.read_exact(&mut txid_bytes)?;
46
47        // Reverse to get display/BE format, then hex-encode
48        txid_bytes.reverse();
49        let source_txid = bytes_to_hex(&txid_bytes);
50
51        // Read output index (4 bytes LE)
52        let source_output_index = read_u32_le(reader)?;
53
54        // Read script (varint length + bytes)
55        let script_len = read_varint(reader)? as usize;
56        let unlocking_script = if script_len > 0 {
57            let mut script_bytes = vec![0u8; script_len];
58            reader.read_exact(&mut script_bytes)?;
59            Some(UnlockingScript::from_binary(&script_bytes))
60        } else {
61            // Read zero-length script as an empty unlocking script
62            Some(UnlockingScript::from_binary(&[]))
63        };
64
65        // Read sequence (4 bytes LE)
66        let sequence = read_u32_le(reader)?;
67
68        Ok(TransactionInput {
69            source_transaction: None,
70            source_txid: Some(source_txid),
71            source_output_index,
72            unlocking_script,
73            sequence,
74        })
75    }
76
77    /// Serialize a transaction input to binary wire format.
78    pub fn to_binary(&self, writer: &mut impl Write) -> Result<(), TransactionError> {
79        // Write TXID in reversed (internal/LE) byte order
80        if let Some(ref txid) = self.source_txid {
81            let mut txid_bytes = hex_to_bytes(txid)
82                .map_err(|e| TransactionError::InvalidFormat(format!("invalid txid hex: {}", e)))?;
83            txid_bytes.reverse();
84            writer.write_all(&txid_bytes)?;
85        } else if let Some(ref source_tx) = self.source_transaction {
86            // hash() returns LE byte order already
87            let hash = source_tx.hash()?;
88            writer.write_all(&hash)?;
89        } else {
90            // Write 32 zero bytes (coinbase or empty)
91            writer.write_all(&[0u8; 32])?;
92        }
93
94        // Write output index
95        write_u32_le(writer, self.source_output_index)?;
96
97        // Write script
98        if let Some(ref script) = self.unlocking_script {
99            let script_bin = script.to_binary();
100            write_varint(writer, script_bin.len() as u64)?;
101            writer.write_all(&script_bin)?;
102        } else {
103            write_varint(writer, 0)?;
104        }
105
106        // Write sequence
107        write_u32_le(writer, self.sequence)?;
108
109        Ok(())
110    }
111}
112
113/// Convert a byte slice to a lowercase hex string.
114fn bytes_to_hex(bytes: &[u8]) -> String {
115    let mut s = String::with_capacity(bytes.len() * 2);
116    for b in bytes {
117        s.push_str(&format!("{:02x}", b));
118    }
119    s
120}
121
122/// Convert a hex string to bytes.
123fn hex_to_bytes(hex: &str) -> Result<Vec<u8>, String> {
124    if !hex.len().is_multiple_of(2) {
125        return Err("odd length hex string".to_string());
126    }
127    let mut bytes = Vec::with_capacity(hex.len() / 2);
128    for i in (0..hex.len()).step_by(2) {
129        let byte = u8::from_str_radix(&hex[i..i + 2], 16)
130            .map_err(|e| format!("invalid hex at position {}: {}", i, e))?;
131        bytes.push(byte);
132    }
133    Ok(bytes)
134}