bsv_wasm/transaction/
mod.rs

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        // Version - 4 bytes
55        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        // In Counter - 1-9 tx_bytes
61        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        // List of Inputs
68        for _ in 0..n_inputs {
69            let tx_in = TxIn::read_in(&mut cursor)?;
70            inputs.push(tx_in);
71        }
72
73        // Out Counter - 1-9 bytes
74        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        // List of  Outputs
80        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        // nLocktime - 4 bytes
87        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        // Version - 4 bytes
105        if let Err(e) = buffer.write_u32::<LittleEndian>(self.version) {
106            return Err(BSVErrors::SerialiseTransaction("version".to_string(), e));
107        }
108
109        // In Counter - 1-9 tx_bytes
110        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        // Inputs
115        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        // Out Counter - 1-9 tx_bytes
125        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        // Outputs
130        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        // nLocktime - 4 bytes
140        if let Err(e) = buffer.write_u32::<LittleEndian>(self.n_locktime) {
141            return Err(BSVErrors::SerialiseTransaction("n_locktime".to_string(), e));
142        }
143
144        // Write out bytes
145        Ok(buffer)
146    }
147
148    /**
149     * Serialises this entire transaction to CBOR, preserving all fields from the standard Transaction format + TX+
150     */
151    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    /**
158     * Deserialises the provided buffer to the TX+ format
159     */
160    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    /**
188     * Gets the ID of the current transaction.
189     * Returns the SHA256d Hash of the current transaction.
190     *
191     * Txid is the reverse of the hash result.
192     */
193    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    /**
202     * Returns all outpoints from this transaction as a 2D array of 36 byte buffers.
203     *
204     * Transaction.get_outpoints()
205     */
206    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/**
223 * Platform Agnostic Functions
224 * ie. Don't need Result<T, E>
225 */
226#[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    /**
264     * Creates a new empty transaction where you need to add inputs and outputs
265     * Transaction.add_input(TxIn) and Transaction.add_output(TxOut)
266     */
267    #[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        // Transaction has been changed, need to recalculate inputs hashes
293        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        // Transaction has been changed, need to recalculate inputs hashes
301        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        // Transaction has been changed, need to recalculate inputs hashes
309        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        // Transaction has been changed, need to recalculate outputs hashes
317        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        // Transaction has been changed, need to recalculate outputs hashes
324        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        // Transaction has been changed, need to recalculate outputs hashes
331        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    /**
352     * XT Method:
353     * Returns the combined sum of all input satoshis.
354     * If any of the inputs dont have satoshis defined, this returns None or null
355     */
356    #[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    /**
368     * Returns the combined sum of all output satoshis.
369     */
370    #[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/**
377 * WASM Specific Functions
378 */
379#[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    /**
439     * Get size of current serialised Transaction object
440     */
441    #[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    /**
450     * Adds an array of TxIn's to the transaction
451     * @param {TxIn[]} tx_ins
452     */
453    #[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    /**
465     * Returns all outpoints from this transaction as a 2D array of 36 byte buffers.
466     *
467     * @returns {Uint8Array[]} outpoint_array
468     */
469    #[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    /**
479     * Adds an array of TxOuts to the transaction
480     * @param {TxOut[]} tx_outs
481     */
482    #[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    /**
494     * Gets the ID of the current transaction as a hex string.
495     */
496    #[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    /**
505     * Gets the ID of the current transaction as a Uint8Array.
506     */
507    #[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    /**
516     * Serialises this entire transaction to CBOR, preserving all fields from the standard Transaction format + TX+
517     */
518    #[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    /**
535     * Deserialises the provided CBOR buffer to the TX+ format
536     */
537    #[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    /**
546     * Deserialises the provided CBOR buffer to the TX+ format
547     */
548    #[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/**
568 * Native Specific Functions
569 */
570#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction")))]
571impl Transaction {
572    /**
573     * Gets the ID of the current transaction as a hex string.
574     */
575    pub fn get_id_hex(&self) -> Result<String, BSVErrors> {
576        Ok(self.get_id_impl()?.to_hex())
577    }
578
579    /**
580     * Gets the ID of the current transaction as a Vec<u8>.
581     */
582    pub fn get_id_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
583        Ok(self.get_id_impl()?.to_bytes())
584    }
585
586    /**
587     * Get size of current serialised Transaction object
588     */
589    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    /**
643     * Serialises this entire transaction to CBOR, preserving all fields from the standard Transaction format + XT
644     */
645    pub fn to_compact_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
646        self.to_compact_bytes_impl()
647    }
648
649    /**
650     * Deserialises the provided CBOR buffer to the XT format
651     */
652    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}