Skip to main content

bsv_transaction/
input.rs

1//! Transaction input referencing a previous output.
2//!
3//! Contains the source transaction ID, output index, unlocking script,
4//! sequence number, and an optional back-reference to the full source
5//! transaction or a direct source output.  Provides binary
6//! serialization/deserialization following the Bitcoin wire format.
7
8use bsv_primitives::util::{BsvReader, BsvWriter, VarInt};
9use bsv_script::Script;
10
11use crate::output::TransactionOutput;
12use crate::TransactionError;
13
14/// Default sequence number indicating a finalized input (no relative lock-time).
15pub const DEFAULT_SEQUENCE_NUMBER: u32 = 0xFFFF_FFFF;
16
17/// A single input in a BSV transaction.
18///
19/// Each input references an output from a previous transaction by its
20/// transaction ID (`source_txid`) and output index (`source_tx_out_index`).
21/// The `unlocking_script` (scriptSig) supplies the data required to satisfy
22/// the referenced output's locking script.
23///
24/// Source output information can be provided either via `source_transaction`
25/// (full previous tx) or `set_source_output` (just the relevant output).
26/// The direct source output takes priority when both are present.
27///
28/// # Wire format (standard)
29///
30/// | Field              | Size             |
31/// |--------------------|------------------|
32/// | source_txid        | 32 bytes (LE)    |
33/// | source_tx_out_index| 4 bytes (LE)     |
34/// | script length      | VarInt           |
35/// | unlocking_script   | variable         |
36/// | sequence_number    | 4 bytes (LE)     |
37#[derive(Clone, Debug)]
38pub struct TransactionInput {
39    /// The 32-byte transaction ID of the output being spent, in internal
40    /// (little-endian) byte order.
41    pub source_txid: [u8; 32],
42
43    /// Index of the output within the source transaction.
44    pub source_tx_out_index: u32,
45
46    /// Sequence number. Defaults to `0xFFFFFFFF` (finalized).
47    pub sequence_number: u32,
48
49    /// The unlocking script (scriptSig) that proves authorization.
50    /// `None` when the input has not yet been signed.
51    pub unlocking_script: Option<Script>,
52
53    /// Optional reference to the full source transaction.
54    /// Used during signing to look up the previous output's locking script
55    /// and satoshi value.
56    pub source_transaction: Option<Box<crate::transaction::Transaction>>,
57
58    /// Optional direct reference to the source output being spent.
59    /// This is an alternative to `source_transaction` when only the
60    /// specific output information (satoshis and locking script) is known.
61    /// Takes priority over `source_transaction` when both are set.
62    source_output: Option<TransactionOutput>,
63}
64
65impl TransactionInput {
66    /// Create a new `TransactionInput` with default values.
67    ///
68    /// The source txid is zeroed, output index is 0, sequence is finalized,
69    /// and no unlocking script or source transaction is set.
70    ///
71    /// # Returns
72    /// A default `TransactionInput`.
73    pub fn new() -> Self {
74        TransactionInput {
75            source_txid: [0u8; 32],
76            source_tx_out_index: 0,
77            sequence_number: DEFAULT_SEQUENCE_NUMBER,
78            unlocking_script: None,
79            source_transaction: None,
80            source_output: None,
81        }
82    }
83
84    /// Deserialize a `TransactionInput` from a `BsvReader`.
85    ///
86    /// Reads the standard wire format: 32-byte txid, 4-byte output index,
87    /// varint-prefixed unlocking script, and 4-byte sequence number.
88    ///
89    /// # Arguments
90    /// * `reader` - The reader positioned at the start of an encoded input.
91    ///
92    /// # Returns
93    /// `Ok(TransactionInput)` on success, or a `TransactionError` if the
94    /// data is truncated or malformed.
95    pub fn read_from(reader: &mut BsvReader) -> Result<Self, TransactionError> {
96        let txid_bytes = reader.read_bytes(32).map_err(|e| {
97            TransactionError::SerializationError(format!("reading source txid: {}", e))
98        })?;
99        let mut source_txid = [0u8; 32];
100        source_txid.copy_from_slice(txid_bytes);
101
102        let source_tx_out_index = reader.read_u32_le().map_err(|e| {
103            TransactionError::SerializationError(format!("reading output index: {}", e))
104        })?;
105
106        let script_len = reader.read_varint().map_err(|e| {
107            TransactionError::SerializationError(format!("reading script length: {}", e))
108        })?;
109
110        let script_bytes = reader
111            .read_bytes(script_len.value() as usize)
112            .map_err(|e| {
113                TransactionError::SerializationError(format!("reading unlocking script: {}", e))
114            })?;
115
116        let sequence_number = reader.read_u32_le().map_err(|e| {
117            TransactionError::SerializationError(format!("reading sequence number: {}", e))
118        })?;
119
120        let unlocking_script = if script_bytes.is_empty() {
121            None
122        } else {
123            Some(Script::from_bytes(script_bytes))
124        };
125
126        Ok(TransactionInput {
127            source_txid,
128            source_tx_out_index,
129            sequence_number,
130            unlocking_script,
131            source_transaction: None,
132            source_output: None,
133        })
134    }
135
136    /// Serialize this `TransactionInput` into a `BsvWriter`.
137    ///
138    /// Writes the standard wire format: txid, output index, varint script
139    /// length, script bytes, and sequence number.
140    ///
141    /// # Arguments
142    /// * `writer` - The writer to append serialized bytes to.
143    pub fn write_to(&self, writer: &mut BsvWriter) {
144        writer.write_bytes(&self.source_txid);
145        writer.write_u32_le(self.source_tx_out_index);
146
147        match &self.unlocking_script {
148            Some(script) => {
149                let script_bytes = script.to_bytes();
150                writer.write_varint(VarInt::from(script_bytes.len()));
151                writer.write_bytes(script_bytes);
152            }
153            None => {
154                writer.write_varint(VarInt::from(0u64));
155            }
156        }
157
158        writer.write_u32_le(self.sequence_number);
159    }
160
161    /// Serialize this input to a byte vector.
162    ///
163    /// If `clear` is true, the unlocking script is omitted (written as
164    /// zero-length). This is used when constructing signature preimages.
165    ///
166    /// # Arguments
167    /// * `clear` - If `true`, omit the unlocking script from the output.
168    ///
169    /// # Returns
170    /// A `Vec<u8>` containing the serialized input.
171    pub fn to_bytes_cleared(&self, clear: bool) -> Vec<u8> {
172        let mut writer = BsvWriter::new();
173        writer.write_bytes(&self.source_txid);
174        writer.write_u32_le(self.source_tx_out_index);
175
176        if clear {
177            writer.write_varint(VarInt::from(0u64));
178        } else if let Some(script) = self.unlocking_script.as_ref() {
179            let script_bytes = script.to_bytes();
180            writer.write_varint(VarInt::from(script_bytes.len()));
181            writer.write_bytes(script_bytes);
182        } else {
183            writer.write_varint(VarInt::from(0u64));
184        }
185
186        writer.write_u32_le(self.sequence_number);
187        writer.into_bytes()
188    }
189
190    /// Set a direct source output on this input.
191    ///
192    /// This provides the satoshi value and locking script of the output
193    /// being spent, without needing the full source transaction.
194    /// Takes priority over `source_transaction` for lookups.
195    ///
196    /// # Arguments
197    /// * `output` - The source output, or `None` to clear.
198    pub fn set_source_output(&mut self, output: Option<TransactionOutput>) {
199        self.source_output = output;
200    }
201
202    /// Look up the source transaction output, if available.
203    ///
204    /// First checks the direct `source_output` field, then falls back
205    /// to looking up the output by index in `source_transaction`.
206    ///
207    /// # Returns
208    /// `Some(&TransactionOutput)` if source info is available, otherwise `None`.
209    pub fn source_tx_output(&self) -> Option<&TransactionOutput> {
210        if let Some(ref output) = self.source_output {
211            return Some(output);
212        }
213        if let Some(ref source_tx) = self.source_transaction {
214            source_tx.outputs.get(self.source_tx_out_index as usize)
215        } else {
216            None
217        }
218    }
219
220    /// Return the satoshi value of the source output, if available.
221    ///
222    /// # Returns
223    /// `Some(satoshis)` if the source output info is available,
224    /// otherwise `None`.
225    pub fn source_tx_satoshis(&self) -> Option<u64> {
226        self.source_tx_output().map(|o| o.satoshis)
227    }
228
229    /// Return the locking script of the source output, if available.
230    ///
231    /// # Returns
232    /// `Some(&Script)` if the source output info is available,
233    /// otherwise `None`.
234    pub fn source_tx_script(&self) -> Option<&Script> {
235        self.source_tx_output().map(|o| &o.locking_script)
236    }
237}
238
239impl Default for TransactionInput {
240    fn default() -> Self {
241        Self::new()
242    }
243}