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}