1use crate::BSVErrors;
2use crate::VarInt;
3use crate::VarIntReader;
4use crate::VarIntWriter;
5use std::io::Cursor;
6use std::io::Read;
7use std::io::Write;
8
9use crate::{
10 utils::{from_reverse_hex, to_reverse_hex},
11 Script,
12};
13use serde::*;
14#[cfg(target_arch = "wasm32")]
15use wasm_bindgen::{prelude::*, throw_str, JsValue};
16
17use byteorder::*;
18
19#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct TxIn {
22 #[serde(serialize_with = "to_reverse_hex", deserialize_with = "from_reverse_hex")]
23 pub(crate) prev_tx_id: Vec<u8>,
24 pub(crate) vout: u32,
25 pub(crate) script_sig: Script,
26 pub(crate) sequence: u32,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
33 pub(crate) unlocking_script: Option<Script>,
34 #[serde(skip_serializing_if = "Option::is_none")]
38 pub(crate) satoshis: Option<u64>,
39}
40
41impl TxIn {
42 pub(crate) fn get_finalised_script_impl(&self) -> Result<Script, BSVErrors> {
43 match self.unlocking_script.as_ref() {
44 Some(unlock_script) => {
46 let mut script_sig_bytes = self.script_sig.to_bytes();
47 let unlock_script_bytes = unlock_script.to_bytes();
48
49 script_sig_bytes.extend_from_slice(&unlock_script_bytes);
50 Script::from_bytes_impl(&script_sig_bytes)
51 }
52 None => Ok(self.script_sig.clone()),
53 }
54 }
55
56 pub(crate) fn from_hex_impl(hex_str: &str) -> Result<TxIn, BSVErrors> {
57 let txin_bytes = hex::decode(hex_str)?;
58
59 let mut cursor = Cursor::new(txin_bytes);
60
61 TxIn::read_in(&mut cursor)
62 }
63
64 pub(crate) fn is_coinbase_outpoint_impl(prev_tx_id: &Vec<u8>, vout: &u32) -> bool {
65 prev_tx_id == &vec![0u8; 32] && vout == &0xFFFFFFFF
66 }
67
68 pub(crate) fn is_coinbase_impl(&self) -> bool {
69 TxIn::is_coinbase_outpoint_impl(&self.prev_tx_id, &self.vout)
70 }
71
72 pub(crate) fn read_in(cursor: &mut Cursor<Vec<u8>>) -> Result<TxIn, BSVErrors> {
73 let mut prev_tx_id = vec![0; 32];
75 if let Err(e) = cursor.read(&mut prev_tx_id) {
76 return Err(BSVErrors::DeserialiseTxIn("prev_tx_id".to_string(), e));
77 }
78 prev_tx_id.reverse();
80
81 let vout = match cursor.read_u32::<LittleEndian>() {
83 Ok(v) => v,
84 Err(e) => return Err(BSVErrors::DeserialiseTxIn("vout".to_string(), e)),
85 };
86
87 let script_sig_size = match cursor.read_varint() {
89 Ok(v) => v,
90 Err(e) => return Err(BSVErrors::DeserialiseTxIn("script_sig_size".to_string(), e)),
91 };
92
93 let mut script_sig = vec![0; script_sig_size as usize];
95 if let Err(e) = cursor.read(&mut script_sig) {
96 return Err(BSVErrors::DeserialiseTxIn("script_sig".to_string(), e));
97 }
98
99 let sequence = match cursor.read_u32::<LittleEndian>() {
101 Ok(v) => v,
102 Err(e) => return Err(BSVErrors::DeserialiseTxIn("sequence".to_string(), e)),
103 };
104
105 Ok(TxIn {
106 script_sig: match TxIn::is_coinbase_outpoint_impl(&prev_tx_id, &vout) {
107 true => Script::from_coinbase_bytes_impl(&script_sig)?,
108 false => Script::from_bytes_impl(&script_sig)?,
109 },
110 prev_tx_id,
111 vout,
112 sequence,
113 satoshis: None,
114 unlocking_script: None,
115 })
116 }
117
118 pub(crate) fn to_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
119 let mut buffer = vec![];
120
121 let mut prev_tx_id = self.prev_tx_id.clone();
123 prev_tx_id.reverse();
124 if let Err(e) = buffer.write(&prev_tx_id) {
126 return Err(BSVErrors::SerialiseTxIn("prev_tx_id".to_string(), e));
127 }
128
129 if let Err(e) = buffer.write_u32::<LittleEndian>(self.vout) {
131 return Err(BSVErrors::SerialiseTxIn("vout".to_string(), e));
132 }
133
134 let finalised_script = self.script_sig.clone();
135
136 if let Err(e) = buffer.write_varint(finalised_script.get_script_length() as u64) {
138 return Err(BSVErrors::SerialiseTxIn("script_sig_size".to_string(), e));
139 }
140
141 if let Err(e) = buffer.write(&finalised_script.to_bytes()) {
143 return Err(BSVErrors::SerialiseTxIn("script_sig".to_string(), e));
144 }
145
146 if let Err(e) = buffer.write_u32::<LittleEndian>(self.sequence) {
148 return Err(BSVErrors::SerialiseTxIn("sequence".to_string(), e));
149 }
150
151 Ok(buffer)
153 }
154
155 pub(crate) fn to_hex_impl(&self) -> Result<String, BSVErrors> {
156 Ok(hex::encode(&self.to_bytes_impl()?))
157 }
158
159 pub(crate) fn to_json_string_impl(&self) -> Result<String, BSVErrors> {
160 let json = serde_json::to_string_pretty(self)?;
161 Ok(json)
162 }
163
164 pub(crate) fn from_outpoint_bytes_impl(outpoint: &[u8]) -> Result<TxIn, BSVErrors> {
165 if outpoint.len() != 36 {
166 return Err(BSVErrors::OutOfBounds("An Outpoint must be precisely 36 bytes long".into()));
167 }
168
169 let mut tx_in = TxIn::default();
170
171 let mut outpoint_bytes = outpoint[0..32].to_vec();
172 outpoint_bytes.reverse();
173
174 let vout = u32::from_le_bytes([outpoint[32], outpoint[33], outpoint[34], outpoint[35]]);
175
176 tx_in.set_prev_tx_id(&outpoint_bytes);
177 tx_in.set_vout(vout);
178
179 Ok(tx_in)
180 }
181
182 pub(crate) fn from_compact_bytes_impl(compact_buffer: &[u8]) -> Result<Self, BSVErrors> {
186 let tx = ciborium::de::from_reader(compact_buffer)?;
187 Ok(tx)
188 }
189
190 pub(crate) fn to_compact_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
194 let mut buffer = vec![];
195 ciborium::ser::into_writer(&self, &mut buffer)?;
196 Ok(buffer)
197 }
198}
199
200#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
205impl TxIn {
206 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(constructor))]
207 pub fn new(prev_tx_id: &[u8], vout: u32, script_sig: &Script, sequence: Option<u32>) -> TxIn {
208 TxIn {
209 prev_tx_id: prev_tx_id.to_vec(),
210 vout,
211 script_sig: script_sig.clone(),
212 sequence: match sequence {
213 Some(v) => v,
214 None => u32::MAX,
215 },
216 satoshis: None,
217 unlocking_script: None,
218 }
219 }
220
221 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
222 pub fn default() -> TxIn {
223 TxIn {
224 prev_tx_id: vec![],
225 satoshis: None,
226 script_sig: Script::default(),
227 sequence: u32::MAX,
228 unlocking_script: None,
229 vout: 0,
230 }
231 }
232
233 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getPrevTxId))]
234 pub fn get_prev_tx_id(&self, little_endian: Option<bool>) -> Vec<u8> {
235 match little_endian {
236 Some(true) => {
237 let mut reversed_tx = self.prev_tx_id.clone();
238 reversed_tx.reverse();
239 reversed_tx
240 }
241 _ => self.prev_tx_id.clone(),
242 }
243 }
244
245 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getPrevTxIdHex))]
246 pub fn get_prev_tx_id_hex(&self, little_endian: Option<bool>) -> String {
247 hex::encode(self.get_prev_tx_id(little_endian))
248 }
249
250 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getVOut))]
251 pub fn get_vout(&self) -> u32 {
252 self.vout
253 }
254
255 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getScriptSigSize))]
256 pub fn get_script_sig_size(&self) -> u64 {
257 self.script_sig.get_script_length() as u64
258 }
259
260 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getScriptSig))]
261 pub fn get_script_sig(&self) -> Script {
262 self.script_sig.clone()
263 }
264
265 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getScriptSigHex))]
266 pub fn get_script_sig_hex(&self) -> String {
267 hex::encode(self.script_sig.to_bytes())
268 }
269
270 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getSequence))]
271 pub fn get_sequence(&self) -> u32 {
272 self.sequence
273 }
274
275 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getSequenceAsBytes))]
276 pub fn get_sequence_as_bytes(&self) -> Vec<u8> {
277 self.sequence.to_be_bytes().to_vec()
278 }
279
280 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getOutpointBytes))]
281 pub fn get_outpoint_bytes(&self, little_endian: Option<bool>) -> Vec<u8> {
282 let mut outpoint_bytes = self.get_prev_tx_id(little_endian);
283 outpoint_bytes.extend_from_slice(&self.vout.to_le_bytes());
284 outpoint_bytes
285 }
286
287 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getOutpointHex))]
288 pub fn get_outpoint_hex(&self, little_endian: Option<bool>) -> String {
289 hex::encode(self.get_outpoint_bytes(little_endian))
290 }
291
292 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setScript))]
293 pub fn set_script(&mut self, script: &Script) {
294 self.script_sig = script.clone();
295 }
296
297 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setPrevTxId))]
298 pub fn set_prev_tx_id(&mut self, txid: &[u8]) {
299 self.prev_tx_id = txid.to_vec();
300 }
301
302 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setVOut))]
303 pub fn set_vout(&mut self, vout: u32) {
304 self.vout = vout;
305 }
306
307 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setSequence))]
308 pub fn set_sequence(&mut self, sequence: u32) {
309 self.sequence = sequence;
310 }
311
312 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setSatoshis))]
313 pub fn set_satoshis(&mut self, satoshis: u64) {
314 self.satoshis = Some(satoshis);
315 }
316
317 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getSatoshis))]
318 pub fn get_satoshis(&self) -> Option<u64> {
319 self.satoshis
320 }
321
322 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setUnlockingScript))]
323 pub fn set_unlocking_script(&mut self, unlocking_script: &Script) {
324 self.unlocking_script = Some(unlocking_script.clone());
325 }
326
327 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getUnlockingScript))]
328 pub fn get_unlocking_script(&self) -> Option<Script> {
329 self.unlocking_script.clone()
330 }
331
332 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getUnlockingScriptBytes))]
333 pub fn get_unlocking_script_bytes(&self) -> Option<Vec<u8>> {
334 self.unlocking_script.as_ref().map(|v| v.to_bytes())
335 }
336}
337
338#[cfg(target_arch = "wasm32")]
342#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
343impl TxIn {
344 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = fromHex))]
345 pub fn from_hex(hex_str: &str) -> Result<TxIn, JsValue> {
346 match TxIn::from_hex_impl(hex_str) {
347 Ok(v) => Ok(v),
348 Err(e) => Err(JsValue::from_str(&e.to_string())),
349 }
350 }
351
352 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toJSON))]
353 pub fn to_json(&self) -> Result<JsValue, JsValue> {
354 match JsValue::from_serde(&self) {
355 Ok(v) => Ok(v),
356 Err(e) => Err(JsValue::from_str(&e.to_string())),
357 }
358 }
359
360 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toString))]
361 pub fn to_json_string(&self) -> Result<String, JsValue> {
362 match TxIn::to_json_string_impl(&self) {
363 Ok(v) => Ok(v),
364 Err(e) => Err(JsValue::from_str(&e.to_string())),
365 }
366 }
367
368 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toBytes))]
369 pub fn to_bytes(&self) -> Result<Vec<u8>, JsValue> {
370 match TxIn::to_bytes_impl(&self) {
371 Ok(v) => Ok(v),
372 Err(e) => Err(JsValue::from_str(&e.to_string())),
373 }
374 }
375
376 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toHex))]
377 pub fn to_hex(&self) -> Result<String, JsValue> {
378 match TxIn::to_hex_impl(&self) {
379 Ok(v) => Ok(v),
380 Err(e) => Err(JsValue::from_str(&e.to_string())),
381 }
382 }
383
384 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = fromOutpointBytes))]
385 pub fn from_outpoint_bytes(outpoint: &[u8]) -> Result<TxIn, JsValue> {
386 match TxIn::from_outpoint_bytes_impl(outpoint) {
387 Ok(v) => Ok(v),
388 Err(e) => Err(JsValue::from_str(&e.to_string())),
389 }
390 }
391
392 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toCompactBytes))]
396 pub fn to_compact_bytes(&self) -> Result<Vec<u8>, JsValue> {
397 match self.to_compact_bytes_impl() {
398 Ok(v) => Ok(v),
399 Err(e) => Err(JsValue::from_str(&e.to_string())),
400 }
401 }
402
403 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = toCompactHex))]
404 pub fn to_compact_hex(&self) -> Result<String, JsValue> {
405 match self.to_compact_bytes_impl() {
406 Ok(v) => Ok(hex::encode(v)),
407 Err(e) => Err(JsValue::from_str(&e.to_string())),
408 }
409 }
410
411 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = fromCompactBytes))]
415 pub fn from_compact_bytes(compact_buffer: &[u8]) -> Result<TxIn, JsValue> {
416 match TxIn::from_compact_bytes_impl(compact_buffer) {
417 Ok(v) => Ok(v),
418 Err(e) => Err(JsValue::from_str(&e.to_string())),
419 }
420 }
421
422 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = fromCompactHex))]
426 pub fn from_compact_hex(compact_hex: String) -> Result<TxIn, JsValue> {
427 let compact_buffer = match hex::decode(compact_hex) {
428 Ok(v) => v,
429 Err(e) => return Err(JsValue::from_str(&e.to_string())),
430 };
431
432 match TxIn::from_compact_bytes_impl(&compact_buffer) {
433 Ok(v) => Ok(v),
434 Err(e) => Err(JsValue::from_str(&e.to_string())),
435 }
436 }
437
438 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getFinalisedScript))]
440 pub fn get_finalised_script(&self) -> Result<Script, JsValue> {
441 match self.get_finalised_script_impl() {
442 Ok(v) => Ok(v),
443 Err(e) => Err(JsValue::from_str(&e.to_string())),
444 }
445 }
446
447 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = isCoinbase))]
449 pub fn is_coinbase(&self) -> bool {
450 self.is_coinbase_impl()
451 }
452}
453
454#[cfg(not(target_arch = "wasm32"))]
458impl TxIn {
459 pub fn from_hex(hex_str: &str) -> Result<TxIn, BSVErrors> {
460 TxIn::from_hex_impl(hex_str)
461 }
462
463 pub fn to_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
464 TxIn::to_bytes_impl(self)
465 }
466
467 pub fn to_hex(&self) -> Result<String, BSVErrors> {
468 TxIn::to_hex_impl(self)
469 }
470
471 pub fn to_compact_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
475 self.to_compact_bytes_impl()
476 }
477
478 pub fn from_compact_bytes(compact_buffer: &[u8]) -> Result<Self, BSVErrors> {
482 TxIn::from_compact_bytes_impl(compact_buffer)
483 }
484
485 pub fn to_compact_hex(&self) -> Result<String, BSVErrors> {
489 Ok(hex::encode(self.to_compact_bytes_impl()?))
490 }
491
492 pub fn from_compact_hex(compact_hex: &str) -> Result<Self, BSVErrors> {
496 let compact_buffer = hex::decode(compact_hex)?;
497 TxIn::from_compact_bytes_impl(&compact_buffer)
498 }
499
500 #[cfg(not(target_arch = "wasm32"))]
501 pub fn to_json_string(&self) -> Result<String, BSVErrors> {
502 TxIn::to_json_string_impl(self)
503 }
504
505 #[cfg(not(target_arch = "wasm32"))]
506 pub fn to_json(&self) -> Result<serde_json::Value, BSVErrors> {
507 let json = serde_json::to_value(self)?;
508 Ok(json)
509 }
510
511 #[cfg(not(target_arch = "wasm32"))]
512 pub fn from_outpoint_bytes(outpoint: &[u8]) -> Result<TxIn, BSVErrors> {
513 TxIn::from_outpoint_bytes_impl(outpoint)
514 }
515
516 pub fn get_finalised_script(&self) -> Result<Script, BSVErrors> {
517 self.get_finalised_script_impl()
518 }
519
520 #[cfg(not(target_arch = "wasm32"))]
522 pub fn is_coinbase(&self) -> bool {
523 self.is_coinbase_impl()
524 }
525}