1use std::io::Cursor;
2use std::io::Write;
3
4use crate::BSVErrors;
5use crate::Hash;
6use crate::VarIntReader;
7use crate::VarIntWriter;
8use byteorder::*;
9use serde::{Deserialize, Serialize};
10#[cfg(target_arch = "wasm32")]
11use wasm_bindgen::{prelude::*, throw_str, JsValue};
12
13mod match_criteria;
14mod sighash;
15mod txin;
16mod txout;
17
18pub use match_criteria::*;
19pub use sighash::*;
20pub use txin::*;
21pub use txout::*;
22
23#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
24#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
25pub struct Transaction {
26 pub(super) version: u32,
27 pub(super) inputs: Vec<TxIn>,
28 pub(super) outputs: Vec<TxOut>,
29 pub(super) n_locktime: u32,
30 #[serde(skip)]
31 pub(super) hash_cache: HashCache,
32}
33
34impl Transaction {
35 pub(crate) fn new_impl(version: u32, inputs: Vec<TxIn>, outputs: Vec<TxOut>, n_locktime: u32) -> Transaction {
36 Transaction {
37 version,
38 inputs,
39 outputs,
40 n_locktime,
41 hash_cache: HashCache::new(),
42 }
43 }
44
45 pub(crate) fn from_hex_impl(hex_str: &str) -> Result<Transaction, BSVErrors> {
46 let tx_bytes = hex::decode(hex_str)?;
47
48 Transaction::from_bytes_impl(&tx_bytes)
49 }
50
51 pub(crate) fn from_bytes_impl(tx_bytes: &[u8]) -> Result<Transaction, BSVErrors> {
52 let mut cursor = Cursor::new(tx_bytes.to_vec());
53
54 let version = match cursor.read_u32::<LittleEndian>() {
56 Ok(v) => v,
57 Err(e) => return Err(BSVErrors::DeserialiseTransaction("version".to_string(), e)),
58 };
59
60 let n_inputs = match cursor.read_varint() {
62 Ok(v) => v,
63 Err(e) => return Err(BSVErrors::DeserialiseTransaction("n_inputs".to_string(), e)),
64 };
65
66 let mut inputs: Vec<TxIn> = Vec::new();
67 for _ in 0..n_inputs {
69 let tx_in = TxIn::read_in(&mut cursor)?;
70 inputs.push(tx_in);
71 }
72
73 let n_outputs = match cursor.read_varint() {
75 Ok(v) => v,
76 Err(e) => return Err(BSVErrors::DeserialiseTransaction("n_outputs".to_string(), e)),
77 };
78
79 let mut outputs: Vec<TxOut> = Vec::new();
81 for _ in 0..n_outputs {
82 let tx_out = TxOut::read_in(&mut cursor)?;
83 outputs.push(tx_out);
84 }
85
86 let n_locktime = match cursor.read_u32::<LittleEndian>() {
88 Ok(v) => v,
89 Err(e) => return Err(BSVErrors::DeserialiseTransaction("n_locktime".to_string(), e)),
90 };
91
92 Ok(Transaction {
93 version,
94 inputs,
95 outputs,
96 n_locktime,
97 hash_cache: HashCache::new(),
98 })
99 }
100
101 pub(crate) fn to_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
102 let mut buffer = Vec::new();
103
104 if let Err(e) = buffer.write_u32::<LittleEndian>(self.version) {
106 return Err(BSVErrors::SerialiseTransaction("version".to_string(), e));
107 }
108
109 if let Err(e) = buffer.write_varint(self.get_ninputs() as u64) {
111 return Err(BSVErrors::SerialiseTransaction("n_inputs".to_string(), e));
112 }
113
114 for i in 0..self.get_ninputs() {
116 let input = &self.inputs[i];
117 let input_bytes = input.to_bytes_impl()?;
118
119 if let Err(e) = buffer.write_all(&input_bytes) {
120 return Err(BSVErrors::SerialiseTransaction(format!("input {}", i), e));
121 }
122 }
123
124 if let Err(e) = buffer.write_varint(self.get_noutputs() as u64) {
126 return Err(BSVErrors::SerialiseTransaction("n_outputs".to_string(), e));
127 }
128
129 for i in 0..self.get_noutputs() {
131 let output = &self.outputs[i as usize];
132 let output_bytes = output.to_bytes_impl()?;
133
134 if let Err(e) = buffer.write_all(&output_bytes) {
135 return Err(BSVErrors::SerialiseTransaction(format!("output {}", i), e));
136 }
137 }
138
139 if let Err(e) = buffer.write_u32::<LittleEndian>(self.n_locktime) {
141 return Err(BSVErrors::SerialiseTransaction("n_locktime".to_string(), e));
142 }
143
144 Ok(buffer)
146 }
147
148 pub(crate) fn to_compact_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
152 let mut buffer = vec![];
153 ciborium::ser::into_writer(&self, &mut buffer)?;
154 Ok(buffer)
155 }
156
157 pub(crate) fn from_compact_bytes_impl(compact_buffer: &[u8]) -> Result<Self, BSVErrors> {
161 let tx = ciborium::de::from_reader(compact_buffer)?;
162 Ok(tx)
163 }
164
165 pub(crate) fn get_size_impl(&self) -> Result<usize, BSVErrors> {
166 let tx_bytes = self.to_bytes_impl()?;
167 Ok(tx_bytes.len())
168 }
169
170 pub(crate) fn to_hex_impl(&self) -> Result<String, BSVErrors> {
171 Ok(hex::encode(&self.to_bytes_impl()?))
172 }
173
174 pub(crate) fn to_compact_hex_impl(&self) -> Result<String, BSVErrors> {
175 Ok(hex::encode(&self.to_compact_bytes_impl()?))
176 }
177
178 pub(crate) fn to_json_string_impl(&self) -> Result<String, BSVErrors> {
179 let json = serde_json::to_string(self)?;
180 Ok(json)
181 }
182
183 pub(crate) fn from_json_string_impl(json_string: &str) -> Result<Transaction, BSVErrors> {
184 Ok(serde_json::from_str(json_string)?)
185 }
186
187 pub(crate) fn get_id_impl(&self) -> Result<Hash, BSVErrors> {
194 let tx_bytes = self.to_bytes_impl()?;
195 let mut hash = Hash::sha_256d(&tx_bytes);
196 hash.0.reverse();
197
198 Ok(hash)
199 }
200
201 pub(crate) fn get_outpoints_impl(&self) -> Vec<Vec<u8>> {
207 self.inputs
208 .iter()
209 .map(|x| {
210 let mut outpoint: Vec<u8> = vec![];
211
212 outpoint.extend(x.prev_tx_id.clone());
213 outpoint.reverse();
214 outpoint.extend(x.vout.to_le_bytes());
215
216 outpoint
217 })
218 .collect()
219 }
220}
221
222#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
227impl Transaction {
228 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getVersion))]
229 pub fn get_version(&self) -> u32 {
230 self.version
231 }
232
233 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getInputsCount))]
234 pub fn get_ninputs(&self) -> usize {
235 self.inputs.len()
236 }
237
238 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getOutputsCount))]
239 pub fn get_noutputs(&self) -> usize {
240 self.outputs.len()
241 }
242
243 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getInput))]
244 pub fn get_input(&self, index: usize) -> Option<TxIn> {
245 self.inputs.get(index).cloned()
246 }
247
248 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getOutput))]
249 pub fn get_output(&self, index: usize) -> Option<TxOut> {
250 self.outputs.get(index).cloned()
251 }
252
253 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getNLocktime))]
254 pub fn get_n_locktime(&self) -> u32 {
255 self.n_locktime
256 }
257
258 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = getNLocktimeAsBytes))]
259 pub fn get_n_locktime_as_bytes(&self) -> Vec<u8> {
260 self.n_locktime.to_be_bytes().to_vec()
261 }
262
263 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(constructor))]
268 pub fn new(version: u32, n_locktime: u32) -> Transaction {
269 Transaction::new_impl(version, vec![], vec![], n_locktime)
270 }
271
272 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
273 pub fn default() -> Transaction {
274 Transaction::new_impl(2, vec![], vec![], 0)
275 }
276
277 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setVersion))]
278 pub fn set_version(&mut self, version: u32) -> Transaction {
279 self.version = version;
280 self.clone()
281 }
282
283 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setNLocktime))]
284 pub fn set_nlocktime(&mut self, n_locktime: u32) -> Transaction {
285 self.n_locktime = n_locktime;
286 self.clone()
287 }
288
289 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = addInput))]
290 pub fn add_input(&mut self, input: &TxIn) {
291 self.inputs.push(input.clone());
292 self.hash_cache.hash_inputs = None;
294 self.hash_cache.hash_sequence = None;
295 }
296
297 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = prependInput))]
298 pub fn prepend_input(&mut self, input: &TxIn) {
299 self.inputs.insert(0, input.clone());
300 self.hash_cache.hash_inputs = None;
302 self.hash_cache.hash_sequence = None;
303 }
304
305 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = insertInput))]
306 pub fn insert_input(&mut self, index: usize, input: &TxIn) {
307 self.inputs.insert(index, input.clone());
308 self.hash_cache.hash_inputs = None;
310 self.hash_cache.hash_sequence = None;
311 }
312
313 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = addOutput))]
314 pub fn add_output(&mut self, output: &TxOut) {
315 self.outputs.push(output.clone());
316 self.hash_cache.hash_outputs = None;
318 }
319
320 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = prependOutput))]
321 pub fn prepend_output(&mut self, output: &TxOut) {
322 self.outputs.insert(0, output.clone());
323 self.hash_cache.hash_outputs = None;
325 }
326
327 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = insertOutput))]
328 pub fn insert_output(&mut self, index: usize, output: &TxOut) {
329 self.outputs.insert(index, output.clone());
330 self.hash_cache.hash_outputs = None;
332 }
333
334 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setInput))]
335 pub fn set_input(&mut self, index: usize, input: &TxIn) {
336 self.inputs[index] = input.clone();
337 }
338
339 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = setOutput))]
340 pub fn set_output(&mut self, index: usize, output: &TxOut) {
341 self.outputs[index] = output.clone();
342 }
343
344 pub fn is_coinbase_impl(&self) -> bool {
345 match (self.get_ninputs(), self.get_input(0)) {
346 (1, Some(x)) => x.is_coinbase_impl(),
347 _ => false,
348 }
349 }
350
351 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = satoshisIn))]
357 pub fn satoshis_in(&self) -> Option<u64> {
358 self.inputs.iter().map(|x| x.satoshis).reduce(|a, b| {
359 if a == None || b == None {
360 return None;
361 }
362
363 Some(a.unwrap() + b.unwrap())
364 })?
365 }
366
367 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(js_name = satoshisOut))]
371 pub fn satoshis_out(&self) -> u64 {
372 self.outputs.iter().map(|x| x.value).sum()
373 }
374}
375
376#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"))]
380#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
381impl Transaction {
382 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromHex))]
383 pub fn from_hex(hex_str: &str) -> Result<Transaction, JsValue> {
384 return match Transaction::from_hex_impl(hex_str) {
385 Ok(v) => Ok(v),
386 Err(e) => Err(JsValue::from_str(&e.to_string())),
387 };
388 }
389
390 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromBytes))]
391 pub fn from_bytes(tx_bytes: &[u8]) -> Result<Transaction, JsValue> {
392 return match Transaction::from_bytes_impl(tx_bytes) {
393 Ok(v) => Ok(v),
394 Err(e) => Err(JsValue::from_str(&e.to_string())),
395 };
396 }
397
398 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toString))]
399 pub fn to_json_string(&self) -> Result<String, JsValue> {
400 match Transaction::to_json_string_impl(&self) {
401 Ok(v) => Ok(v),
402 Err(e) => Err(JsValue::from_str(&e.to_string())),
403 }
404 }
405
406 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromJsonString))]
407 pub fn from_json_string(json_string: &str) -> Result<Transaction, JsValue> {
408 match Transaction::from_json_string_impl(json_string) {
409 Ok(v) => Ok(v),
410 Err(e) => Err(JsValue::from_str(&e.to_string())),
411 }
412 }
413
414 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toJSON))]
415 pub fn to_json(&self) -> Result<JsValue, JsValue> {
416 match JsValue::from_serde(&self) {
417 Ok(v) => Ok(v),
418 Err(e) => Err(JsValue::from_str(&e.to_string())),
419 }
420 }
421
422 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toBytes))]
423 pub fn to_bytes(&self) -> Result<Vec<u8>, JsValue> {
424 match Transaction::to_bytes_impl(&self) {
425 Ok(v) => Ok(v),
426 Err(e) => Err(JsValue::from_str(&e.to_string())),
427 }
428 }
429
430 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toHex))]
431 pub fn to_hex(&self) -> Result<String, JsValue> {
432 match Transaction::to_hex_impl(&self) {
433 Ok(v) => Ok(v),
434 Err(e) => Err(JsValue::from_str(&e.to_string())),
435 }
436 }
437
438 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = getSize))]
442 pub fn get_size(&self) -> Result<usize, JsValue> {
443 match Transaction::get_size_impl(&self) {
444 Ok(v) => Ok(v),
445 Err(e) => Err(JsValue::from_str(&e.to_string())),
446 }
447 }
448
449 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = addInputs))]
454 pub fn add_inputs(&mut self, tx_ins: Box<[JsValue]>) {
455 let js_value = &*tx_ins.to_vec();
456
457 for elem in js_value {
458 let input = elem.into_serde().unwrap();
459
460 self.add_input(&input);
461 }
462 }
463
464 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = getOutpoints))]
470 pub fn get_outpoints(&mut self) -> Result<JsValue, JsValue> {
471 let outpoints = self.get_outpoints_impl();
472 match JsValue::from_serde(&outpoints) {
473 Ok(v) => Ok(v),
474 Err(e) => Err(JsValue::from_str(&e.to_string())),
475 }
476 }
477
478 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = addOutputs))]
483 pub fn add_outputs(&mut self, tx_outs: Box<[JsValue]>) {
484 let js_value = &*tx_outs.to_vec();
485
486 for elem in js_value {
487 let output = elem.into_serde().unwrap();
488
489 self.add_output(&output);
490 }
491 }
492
493 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = getIdHex))]
497 pub fn get_id_hex(&self) -> Result<String, JsValue> {
498 match self.get_id_impl() {
499 Ok(v) => Ok(v.to_hex()),
500 Err(e) => Err(JsValue::from_str(&e.to_string())),
501 }
502 }
503
504 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = getIdBytes))]
508 pub fn get_id_bytes(&self) -> Result<Vec<u8>, JsValue> {
509 match self.get_id_impl() {
510 Ok(v) => Ok(v.to_bytes()),
511 Err(e) => Err(JsValue::from_str(&e.to_string())),
512 }
513 }
514
515 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toCompactBytes))]
519 pub fn to_compact_bytes(&self) -> Result<Vec<u8>, JsValue> {
520 match self.to_compact_bytes_impl() {
521 Ok(v) => Ok(v),
522 Err(e) => Err(JsValue::from_str(&e.to_string())),
523 }
524 }
525
526 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toCompactHex))]
527 pub fn to_compact_hex(&self) -> Result<String, JsValue> {
528 match Transaction::to_compact_hex_impl(&self) {
529 Ok(v) => Ok(v),
530 Err(e) => Err(JsValue::from_str(&e.to_string())),
531 }
532 }
533
534 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromCompactBytes))]
538 pub fn from_compact_bytes(compact_buffer: &[u8]) -> Result<Transaction, JsValue> {
539 match Transaction::from_compact_bytes_impl(compact_buffer) {
540 Ok(v) => Ok(v),
541 Err(e) => Err(JsValue::from_str(&e.to_string())),
542 }
543 }
544
545 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromCompactHex))]
549 pub fn from_compact_hex(compact_hex: String) -> Result<Transaction, JsValue> {
550 let compact_buffer = match hex::decode(compact_hex) {
551 Ok(v) => v,
552 Err(e) => return Err(JsValue::from_str(&e.to_string())),
553 };
554
555 match Transaction::from_compact_bytes_impl(&compact_buffer) {
556 Ok(v) => Ok(v),
557 Err(e) => Err(JsValue::from_str(&e.to_string())),
558 }
559 }
560
561 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = isCoinbase))]
562 pub fn is_coinbase(&self) -> bool {
563 self.is_coinbase_impl()
564 }
565}
566
567#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction")))]
571impl Transaction {
572 pub fn get_id_hex(&self) -> Result<String, BSVErrors> {
576 Ok(self.get_id_impl()?.to_hex())
577 }
578
579 pub fn get_id_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
583 Ok(self.get_id_impl()?.to_bytes())
584 }
585
586 pub fn get_size(&self) -> Result<usize, BSVErrors> {
590 self.get_size_impl()
591 }
592
593 pub fn from_hex(hex_str: &str) -> Result<Transaction, BSVErrors> {
594 Transaction::from_hex_impl(hex_str)
595 }
596
597 pub fn from_bytes(tx_bytes: &[u8]) -> Result<Transaction, BSVErrors> {
598 Transaction::from_bytes_impl(tx_bytes)
599 }
600
601 pub fn to_json_string(&self) -> Result<String, BSVErrors> {
602 Transaction::to_json_string_impl(self)
603 }
604
605 pub fn from_json_string(json_string: &str) -> Result<Transaction, BSVErrors> {
606 Transaction::from_json_string_impl(json_string)
607 }
608
609 pub fn to_json(&self) -> Result<serde_json::Value, BSVErrors> {
610 let json = serde_json::to_value(self)?;
611 Ok(json)
612 }
613
614 pub fn to_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
615 Transaction::to_bytes_impl(self)
616 }
617
618 pub fn to_hex(&self) -> Result<String, BSVErrors> {
619 Transaction::to_hex_impl(self)
620 }
621
622 pub fn to_compact_hex(&self) -> Result<String, BSVErrors> {
623 Transaction::to_compact_hex_impl(self)
624 }
625
626 pub fn add_inputs(&mut self, tx_ins: Vec<TxIn>) {
627 for txin in tx_ins {
628 self.add_input(&txin);
629 }
630 }
631
632 pub fn add_outputs(&mut self, tx_outs: Vec<TxOut>) {
633 for txout in tx_outs {
634 self.add_output(&txout);
635 }
636 }
637
638 pub fn get_outpoints(&mut self) -> Vec<Vec<u8>> {
639 self.get_outpoints_impl()
640 }
641
642 pub fn to_compact_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
646 self.to_compact_bytes_impl()
647 }
648
649 pub fn from_compact_bytes(compact_buffer: &[u8]) -> Result<Self, BSVErrors> {
653 Transaction::from_compact_bytes_impl(compact_buffer)
654 }
655
656 pub fn from_compact_hex(compact_hex: &str) -> Result<Self, BSVErrors> {
657 Transaction::from_compact_bytes_impl(&hex::decode(compact_hex)?)
658 }
659
660 pub fn is_coinbase(&self) -> bool {
661 self.is_coinbase_impl()
662 }
663}