bitcoinkernel/core/
transaction.rs

1use std::{ffi::c_void, marker::PhantomData};
2
3use libbitcoinkernel_sys::{
4    btck_Transaction, btck_TransactionInput, btck_TransactionOutPoint, btck_TransactionOutput,
5    btck_Txid, btck_transaction_copy, btck_transaction_count_inputs,
6    btck_transaction_count_outputs, btck_transaction_create, btck_transaction_destroy,
7    btck_transaction_get_input_at, btck_transaction_get_output_at, btck_transaction_get_txid,
8    btck_transaction_input_copy, btck_transaction_input_destroy,
9    btck_transaction_input_get_out_point, btck_transaction_out_point_copy,
10    btck_transaction_out_point_destroy, btck_transaction_out_point_get_index,
11    btck_transaction_out_point_get_txid, btck_transaction_output_copy,
12    btck_transaction_output_create, btck_transaction_output_destroy,
13    btck_transaction_output_get_amount, btck_transaction_output_get_script_pubkey,
14    btck_transaction_to_bytes, btck_txid_copy, btck_txid_destroy, btck_txid_equals,
15    btck_txid_to_bytes,
16};
17
18use crate::{
19    c_serialize,
20    ffi::{
21        c_helpers::present,
22        sealed::{AsPtr, FromMutPtr, FromPtr},
23    },
24    KernelError, ScriptPubkeyExt,
25};
26
27use super::script::ScriptPubkeyRef;
28
29/// Common operations for transactions, implemented by both owned and borrowed types.
30pub trait TransactionExt: AsPtr<btck_Transaction> {
31    /// Returns the number of outputs in this transaction.
32    fn output_count(&self) -> usize {
33        unsafe { btck_transaction_count_outputs(self.as_ptr()) as usize }
34    }
35
36    /// Returns a reference to the output at the specified index.
37    ///
38    /// # Arguments
39    /// * `index` - The zero-based index of the output to retrieve
40    ///
41    /// # Returns
42    /// * `Ok(RefType<TxOut, Transaction>)` - A reference to the output
43    /// * `Err(KernelError::OutOfBounds)` - If the index is invalid
44    fn output(&self, index: usize) -> Result<TxOutRef<'_>, KernelError> {
45        if index >= self.output_count() {
46            return Err(KernelError::OutOfBounds);
47        }
48
49        let tx_out_ref =
50            unsafe { TxOutRef::from_ptr(btck_transaction_get_output_at(self.as_ptr(), index)) };
51
52        Ok(tx_out_ref)
53    }
54
55    fn input_count(&self) -> usize {
56        unsafe { btck_transaction_count_inputs(self.as_ptr()) as usize }
57    }
58
59    /// Returns a reference to the input at the specified index.
60    ///
61    /// # Arguments
62    /// * `index` - The zero-based index of the input to retrieve
63    ///
64    /// # Returns
65    /// * `Ok(TxInRef)` - A reference to the input
66    /// * `Err(KernelError::OutOfBounds)` - If the index is invalid
67    fn input(&self, index: usize) -> Result<TxInRef<'_>, KernelError> {
68        if index >= self.input_count() {
69            return Err(KernelError::OutOfBounds);
70        }
71
72        let tx_in_ref =
73            unsafe { TxInRef::from_ptr(btck_transaction_get_input_at(self.as_ptr(), index)) };
74        Ok(tx_in_ref)
75    }
76
77    /// Returns a reference to the transaction ID (txid) of this transaction.
78    fn txid(&self) -> TxidRef<'_> {
79        let ptr = unsafe { btck_transaction_get_txid(self.as_ptr()) };
80        unsafe { TxidRef::from_ptr(ptr) }
81    }
82
83    /// Consensus encodes the transaction to Bitcoin wire format.
84    fn consensus_encode(&self) -> Result<Vec<u8>, KernelError> {
85        c_serialize(|callback, user_data| unsafe {
86            btck_transaction_to_bytes(self.as_ptr(), Some(callback), user_data)
87        })
88    }
89
90    /// Returns an iterator over all inputs in this transaction.
91    fn inputs(&self) -> TxInIter<'_> {
92        TxInIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) })
93    }
94
95    /// Returns an iterator over all outputs in this transaction.
96    fn outputs(&self) -> TxOutIter<'_> {
97        TxOutIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) })
98    }
99}
100
101/// A Bitcoin transaction.
102pub struct Transaction {
103    inner: *mut btck_Transaction,
104}
105
106unsafe impl Send for Transaction {}
107unsafe impl Sync for Transaction {}
108
109impl Transaction {
110    pub fn new(transaction_bytes: &[u8]) -> Result<Self, KernelError> {
111        let inner = unsafe {
112            btck_transaction_create(
113                transaction_bytes.as_ptr() as *const c_void,
114                transaction_bytes.len(),
115            )
116        };
117
118        if inner.is_null() {
119            Err(KernelError::Internal(
120                "Failed to create transaction from bytes".to_string(),
121            ))
122        } else {
123            Ok(Transaction { inner })
124        }
125    }
126
127    pub fn as_ref(&self) -> TransactionRef<'_> {
128        unsafe { TransactionRef::from_ptr(self.inner as *const _) }
129    }
130}
131
132impl AsPtr<btck_Transaction> for Transaction {
133    fn as_ptr(&self) -> *const btck_Transaction {
134        self.inner as *const _
135    }
136}
137
138impl FromMutPtr<btck_Transaction> for Transaction {
139    unsafe fn from_ptr(ptr: *mut btck_Transaction) -> Self {
140        Transaction { inner: ptr }
141    }
142}
143
144impl TransactionExt for Transaction {}
145
146impl Clone for Transaction {
147    fn clone(&self) -> Self {
148        Transaction {
149            inner: unsafe { btck_transaction_copy(self.inner) },
150        }
151    }
152}
153
154impl Drop for Transaction {
155    fn drop(&mut self) {
156        unsafe { btck_transaction_destroy(self.inner) }
157    }
158}
159
160impl TryFrom<&[u8]> for Transaction {
161    type Error = KernelError;
162
163    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
164        Transaction::new(bytes)
165    }
166}
167
168impl TryFrom<Transaction> for Vec<u8> {
169    type Error = KernelError;
170
171    fn try_from(transaction: Transaction) -> Result<Self, Self::Error> {
172        transaction.consensus_encode()
173    }
174}
175
176impl TryFrom<&Transaction> for Vec<u8> {
177    type Error = KernelError;
178
179    fn try_from(transaction: &Transaction) -> Result<Self, Self::Error> {
180        transaction.consensus_encode()
181    }
182}
183
184pub struct TransactionRef<'a> {
185    inner: *const btck_Transaction,
186    marker: PhantomData<&'a ()>,
187}
188
189unsafe impl<'a> Send for TransactionRef<'a> {}
190unsafe impl<'a> Sync for TransactionRef<'a> {}
191
192impl<'a> TransactionRef<'a> {
193    pub fn to_owned(&self) -> Transaction {
194        Transaction {
195            inner: unsafe { btck_transaction_copy(self.inner) },
196        }
197    }
198}
199
200impl<'a> AsPtr<btck_Transaction> for TransactionRef<'a> {
201    fn as_ptr(&self) -> *const btck_Transaction {
202        self.inner
203    }
204}
205
206impl<'a> FromPtr<btck_Transaction> for TransactionRef<'a> {
207    unsafe fn from_ptr(ptr: *const btck_Transaction) -> Self {
208        TransactionRef {
209            inner: ptr,
210            marker: PhantomData,
211        }
212    }
213}
214impl<'a> TransactionExt for TransactionRef<'a> {}
215
216impl<'a> Clone for TransactionRef<'a> {
217    fn clone(&self) -> Self {
218        *self
219    }
220}
221
222impl<'a> Copy for TransactionRef<'a> {}
223
224pub struct TxInIter<'a> {
225    transaction: TransactionRef<'a>,
226    current_index: usize,
227}
228
229impl<'a> TxInIter<'a> {
230    fn new(transaction: TransactionRef<'a>) -> Self {
231        Self {
232            transaction,
233            current_index: 0,
234        }
235    }
236}
237
238impl<'a> Iterator for TxInIter<'a> {
239    type Item = TxInRef<'a>;
240
241    fn next(&mut self) -> Option<Self::Item> {
242        if self.current_index >= self.transaction.input_count() {
243            return None;
244        }
245
246        let index = self.current_index;
247        self.current_index += 1;
248
249        let tx_in_ptr = unsafe { btck_transaction_get_input_at(self.transaction.as_ptr(), index) };
250
251        if tx_in_ptr.is_null() {
252            None
253        } else {
254            Some(unsafe { TxInRef::from_ptr(tx_in_ptr) })
255        }
256    }
257
258    fn size_hint(&self) -> (usize, Option<usize>) {
259        let remaining = self
260            .transaction
261            .input_count()
262            .saturating_sub(self.current_index);
263        (remaining, Some(remaining))
264    }
265}
266
267impl<'a> ExactSizeIterator for TxInIter<'a> {
268    fn len(&self) -> usize {
269        self.transaction
270            .input_count()
271            .saturating_sub(self.current_index)
272    }
273}
274
275pub struct TxOutIter<'a> {
276    transaction: TransactionRef<'a>,
277    current_index: usize,
278}
279
280impl<'a> TxOutIter<'a> {
281    fn new(transaction: TransactionRef<'a>) -> Self {
282        Self {
283            transaction,
284            current_index: 0,
285        }
286    }
287}
288
289impl<'a> Iterator for TxOutIter<'a> {
290    type Item = TxOutRef<'a>;
291
292    fn next(&mut self) -> Option<Self::Item> {
293        if self.current_index >= self.transaction.output_count() {
294            return None;
295        }
296
297        let index = self.current_index;
298        self.current_index += 1;
299
300        let tx_out_ptr =
301            unsafe { btck_transaction_get_output_at(self.transaction.as_ptr(), index) };
302
303        if tx_out_ptr.is_null() {
304            None
305        } else {
306            Some(unsafe { TxOutRef::from_ptr(tx_out_ptr) })
307        }
308    }
309
310    fn size_hint(&self) -> (usize, Option<usize>) {
311        let remaining = self
312            .transaction
313            .input_count()
314            .saturating_sub(self.current_index);
315        (remaining, Some(remaining))
316    }
317}
318
319impl<'a> ExactSizeIterator for TxOutIter<'a> {
320    fn len(&self) -> usize {
321        self.transaction
322            .output_count()
323            .saturating_sub(self.current_index)
324    }
325}
326
327/// Common operations for transaction outputs, implemented by both owned and borrowed types.
328pub trait TxOutExt: AsPtr<btck_TransactionOutput> {
329    /// Returns the amount of this output in satoshis.
330    fn value(&self) -> i64 {
331        unsafe { btck_transaction_output_get_amount(self.as_ptr()) }
332    }
333
334    /// Returns a reference to the script pubkey that defines how this output can be spent.
335    ///
336    /// # Returns
337    /// * `RefType<ScriptPubkey, TxOut>` - A reference to the script pubkey
338    fn script_pubkey(&self) -> ScriptPubkeyRef<'_> {
339        let ptr = unsafe { btck_transaction_output_get_script_pubkey(self.as_ptr()) };
340        unsafe { ScriptPubkeyRef::from_ptr(ptr) }
341    }
342}
343
344/// A single transaction output containing a value and spending conditions.
345///
346/// Transaction outputs can be created from a script pubkey and amount, or retrieved
347/// from existing transactions. They represent spendable coins in the UTXO set.
348#[derive(Debug)]
349pub struct TxOut {
350    inner: *mut btck_TransactionOutput,
351}
352
353unsafe impl Send for TxOut {}
354unsafe impl Sync for TxOut {}
355
356impl TxOut {
357    /// Creates a new transaction output with the specified script and amount.
358    ///
359    /// # Arguments
360    /// * `script_pubkey` - The script defining how this output can be spent
361    /// * `amount` - The amount in satoshis
362    pub fn new(script_pubkey: &impl ScriptPubkeyExt, amount: i64) -> Self {
363        TxOut {
364            inner: unsafe { btck_transaction_output_create(script_pubkey.as_ptr(), amount) },
365        }
366    }
367
368    pub fn as_ref(&self) -> TxOutRef<'_> {
369        unsafe { TxOutRef::from_ptr(self.inner as *const _) }
370    }
371}
372
373impl AsPtr<btck_TransactionOutput> for TxOut {
374    fn as_ptr(&self) -> *const btck_TransactionOutput {
375        self.inner as *const _
376    }
377}
378
379impl FromMutPtr<btck_TransactionOutput> for TxOut {
380    unsafe fn from_ptr(ptr: *mut btck_TransactionOutput) -> Self {
381        TxOut { inner: ptr }
382    }
383}
384
385impl TxOutExt for TxOut {}
386
387impl Clone for TxOut {
388    fn clone(&self) -> Self {
389        TxOut {
390            inner: unsafe { btck_transaction_output_copy(self.inner) },
391        }
392    }
393}
394
395impl Drop for TxOut {
396    fn drop(&mut self) {
397        unsafe { btck_transaction_output_destroy(self.inner) }
398    }
399}
400
401pub struct TxOutRef<'a> {
402    inner: *const btck_TransactionOutput,
403    marker: PhantomData<&'a ()>,
404}
405
406unsafe impl<'a> Send for TxOutRef<'a> {}
407unsafe impl<'a> Sync for TxOutRef<'a> {}
408
409impl<'a> TxOutRef<'a> {
410    pub fn to_owned(&self) -> TxOut {
411        TxOut {
412            inner: unsafe { btck_transaction_output_copy(self.inner) },
413        }
414    }
415}
416
417impl<'a> AsPtr<btck_TransactionOutput> for TxOutRef<'a> {
418    fn as_ptr(&self) -> *const btck_TransactionOutput {
419        self.inner as *const _
420    }
421}
422
423impl<'a> FromPtr<btck_TransactionOutput> for TxOutRef<'a> {
424    unsafe fn from_ptr(ptr: *const btck_TransactionOutput) -> Self {
425        TxOutRef {
426            inner: ptr,
427            marker: PhantomData,
428        }
429    }
430}
431
432impl<'a> TxOutExt for TxOutRef<'a> {}
433
434impl<'a> Clone for TxOutRef<'a> {
435    fn clone(&self) -> Self {
436        *self
437    }
438}
439
440impl<'a> Copy for TxOutRef<'a> {}
441
442/// Common operations for transaction inputs, implemented by both owned and borrowed types.
443pub trait TxInExt: AsPtr<btck_TransactionInput> {
444    /// Returns a reference to the outpoint being spent by this input.
445    ///
446    /// The outpoint identifies which previous transaction output this input is spending.
447    fn outpoint(&self) -> TxOutPointRef<'_> {
448        let ptr = unsafe { btck_transaction_input_get_out_point(self.as_ptr()) };
449        unsafe { TxOutPointRef::from_ptr(ptr) }
450    }
451}
452
453/// A single transaction input referencing a previous output to be spent.
454#[derive(Debug)]
455pub struct TxIn {
456    inner: *mut btck_TransactionInput,
457}
458
459unsafe impl Send for TxIn {}
460unsafe impl Sync for TxIn {}
461
462impl TxIn {
463    pub fn as_ref(&self) -> TxInRef<'_> {
464        unsafe { TxInRef::from_ptr(self.inner as *const _) }
465    }
466}
467
468impl AsPtr<btck_TransactionInput> for TxIn {
469    fn as_ptr(&self) -> *const btck_TransactionInput {
470        self.inner as *const _
471    }
472}
473
474impl FromMutPtr<btck_TransactionInput> for TxIn {
475    unsafe fn from_ptr(ptr: *mut btck_TransactionInput) -> Self {
476        TxIn { inner: ptr }
477    }
478}
479
480impl TxInExt for TxIn {}
481
482impl Clone for TxIn {
483    fn clone(&self) -> Self {
484        TxIn {
485            inner: unsafe { btck_transaction_input_copy(self.inner) },
486        }
487    }
488}
489
490impl Drop for TxIn {
491    fn drop(&mut self) {
492        unsafe { btck_transaction_input_destroy(self.inner) }
493    }
494}
495
496pub struct TxInRef<'a> {
497    inner: *const btck_TransactionInput,
498    marker: PhantomData<&'a ()>,
499}
500
501unsafe impl<'a> Send for TxInRef<'a> {}
502unsafe impl<'a> Sync for TxInRef<'a> {}
503
504impl<'a> TxInRef<'a> {
505    /// Creates an owned copy of this transaction input.
506    pub fn to_owned(&self) -> TxIn {
507        TxIn {
508            inner: unsafe { btck_transaction_input_copy(self.inner) },
509        }
510    }
511}
512
513impl<'a> AsPtr<btck_TransactionInput> for TxInRef<'a> {
514    fn as_ptr(&self) -> *const btck_TransactionInput {
515        self.inner as *const _
516    }
517}
518
519impl<'a> FromPtr<btck_TransactionInput> for TxInRef<'a> {
520    unsafe fn from_ptr(ptr: *const btck_TransactionInput) -> Self {
521        TxInRef {
522            inner: ptr,
523            marker: PhantomData,
524        }
525    }
526}
527
528impl<'a> TxInExt for TxInRef<'a> {}
529
530impl<'a> Clone for TxInRef<'a> {
531    fn clone(&self) -> Self {
532        *self
533    }
534}
535
536impl<'a> Copy for TxInRef<'a> {}
537
538/// Common operations for transaction out points, implemented by both owned and borrowed types.
539pub trait TxOutPointExt: AsPtr<btck_TransactionOutPoint> {
540    /// Returns the output index within the referenced transaction.
541    ///
542    /// This is the zero-based index of the output in the transaction's output list.
543    fn index(&self) -> u32 {
544        unsafe { btck_transaction_out_point_get_index(self.as_ptr()) }
545    }
546
547    /// Returns a reference to the transaction ID of the transaction containing this output.
548    fn txid(&self) -> TxidRef<'_> {
549        let ptr = unsafe { btck_transaction_out_point_get_txid(self.as_ptr()) };
550        unsafe { TxidRef::from_ptr(ptr) }
551    }
552}
553
554/// A reference to a specific output in a previous transaction.
555///
556/// An outpoint uniquely identifies a transaction output by combining a transaction ID
557/// with an output index. Outpoints are used in transaction inputs to specify which
558/// previous outputs are being spent.
559#[derive(Debug)]
560pub struct TxOutPoint {
561    inner: *mut btck_TransactionOutPoint,
562}
563
564unsafe impl Send for TxOutPoint {}
565unsafe impl Sync for TxOutPoint {}
566
567impl TxOutPoint {
568    /// Returns a borrowed reference to this outpoint.
569    pub fn as_ref(&self) -> TxOutPointRef<'_> {
570        unsafe { TxOutPointRef::from_ptr(self.inner as *const _) }
571    }
572}
573
574impl AsPtr<btck_TransactionOutPoint> for TxOutPoint {
575    fn as_ptr(&self) -> *const btck_TransactionOutPoint {
576        self.inner as *const _
577    }
578}
579
580impl FromMutPtr<btck_TransactionOutPoint> for TxOutPoint {
581    unsafe fn from_ptr(ptr: *mut btck_TransactionOutPoint) -> Self {
582        TxOutPoint { inner: ptr }
583    }
584}
585
586impl TxOutPointExt for TxOutPoint {}
587
588impl Clone for TxOutPoint {
589    fn clone(&self) -> Self {
590        TxOutPoint {
591            inner: unsafe { btck_transaction_out_point_copy(self.inner) },
592        }
593    }
594}
595
596impl Drop for TxOutPoint {
597    fn drop(&mut self) {
598        unsafe { btck_transaction_out_point_destroy(self.inner) }
599    }
600}
601
602pub struct TxOutPointRef<'a> {
603    inner: *const btck_TransactionOutPoint,
604    marker: PhantomData<&'a ()>,
605}
606
607unsafe impl<'a> Send for TxOutPointRef<'a> {}
608unsafe impl<'a> Sync for TxOutPointRef<'a> {}
609
610impl<'a> TxOutPointRef<'a> {
611    /// Creates an owned copy of this outpoint.
612    pub fn to_owned(&self) -> TxOutPoint {
613        TxOutPoint {
614            inner: unsafe { btck_transaction_out_point_copy(self.inner) },
615        }
616    }
617}
618
619impl<'a> AsPtr<btck_TransactionOutPoint> for TxOutPointRef<'a> {
620    fn as_ptr(&self) -> *const btck_TransactionOutPoint {
621        self.inner as *const _
622    }
623}
624
625impl<'a> FromPtr<btck_TransactionOutPoint> for TxOutPointRef<'a> {
626    unsafe fn from_ptr(ptr: *const btck_TransactionOutPoint) -> Self {
627        TxOutPointRef {
628            inner: ptr,
629            marker: PhantomData,
630        }
631    }
632}
633
634impl<'a> TxOutPointExt for TxOutPointRef<'a> {}
635
636impl<'a> Clone for TxOutPointRef<'a> {
637    fn clone(&self) -> Self {
638        *self
639    }
640}
641
642impl<'a> Copy for TxOutPointRef<'a> {}
643
644/// Common operations for transaction IDs, implemented by both owned and borrowed types.
645pub trait TxidExt: AsPtr<btck_Txid> {
646    /// Serializes the txid to raw bytes.
647    fn to_bytes(&self) -> [u8; 32] {
648        let mut bytes = [0u8; 32];
649        unsafe {
650            btck_txid_to_bytes(self.as_ptr(), bytes.as_mut_ptr());
651        }
652        bytes
653    }
654}
655
656#[derive(Debug)]
657pub struct Txid {
658    inner: *mut btck_Txid,
659}
660
661unsafe impl Send for Txid {}
662unsafe impl Sync for Txid {}
663
664impl Txid {
665    pub fn as_ref(&self) -> TxidRef<'_> {
666        unsafe { TxidRef::from_ptr(self.inner as *const _) }
667    }
668}
669
670impl AsPtr<btck_Txid> for Txid {
671    fn as_ptr(&self) -> *const btck_Txid {
672        self.inner as *const _
673    }
674}
675
676impl FromMutPtr<btck_Txid> for Txid {
677    unsafe fn from_ptr(ptr: *mut btck_Txid) -> Self {
678        Txid { inner: ptr }
679    }
680}
681
682impl TxidExt for Txid {}
683
684impl Clone for Txid {
685    fn clone(&self) -> Self {
686        Txid {
687            inner: unsafe { btck_txid_copy(self.inner) },
688        }
689    }
690}
691
692impl Drop for Txid {
693    fn drop(&mut self) {
694        unsafe { btck_txid_destroy(self.inner) }
695    }
696}
697
698impl PartialEq for Txid {
699    fn eq(&self, other: &Self) -> bool {
700        present(unsafe { btck_txid_equals(self.inner, other.inner) })
701    }
702}
703
704impl PartialEq<TxidRef<'_>> for Txid {
705    fn eq(&self, other: &TxidRef<'_>) -> bool {
706        present(unsafe { btck_txid_equals(self.inner, other.inner) })
707    }
708}
709
710impl Eq for Txid {}
711
712#[derive(Debug)]
713pub struct TxidRef<'a> {
714    inner: *const btck_Txid,
715    marker: PhantomData<&'a ()>,
716}
717
718unsafe impl<'a> Send for TxidRef<'a> {}
719unsafe impl<'a> Sync for TxidRef<'a> {}
720
721impl<'a> TxidRef<'a> {
722    pub fn to_owned(&self) -> Txid {
723        Txid {
724            inner: unsafe { btck_txid_copy(self.inner) },
725        }
726    }
727}
728
729impl<'a> AsPtr<btck_Txid> for TxidRef<'a> {
730    fn as_ptr(&self) -> *const btck_Txid {
731        self.inner as *const _
732    }
733}
734
735impl<'a> FromPtr<btck_Txid> for TxidRef<'a> {
736    unsafe fn from_ptr(ptr: *const btck_Txid) -> Self {
737        TxidRef {
738            inner: ptr,
739            marker: PhantomData,
740        }
741    }
742}
743
744impl<'a> TxidExt for TxidRef<'a> {}
745
746impl<'a> Clone for TxidRef<'a> {
747    fn clone(&self) -> Self {
748        *self
749    }
750}
751
752impl<'a> Copy for TxidRef<'a> {}
753
754impl<'a> PartialEq for TxidRef<'a> {
755    fn eq(&self, other: &Self) -> bool {
756        present(unsafe { btck_txid_equals(self.inner, other.inner) })
757    }
758}
759
760impl<'a> Eq for TxidRef<'a> {}
761
762impl PartialEq<Txid> for TxidRef<'_> {
763    fn eq(&self, other: &Txid) -> bool {
764        present(unsafe { btck_txid_equals(self.inner, other.inner) })
765    }
766}
767
768#[cfg(test)]
769mod tests {
770    use super::*;
771    use crate::ffi::test_utils::{
772        test_owned_clone_and_send, test_owned_trait_requirements, test_ref_copy,
773        test_ref_trait_requirements,
774    };
775    use crate::{Block, ScriptPubkey};
776    use std::fs::File;
777    use std::io::{BufRead, BufReader};
778
779    fn read_block_data() -> Vec<Vec<u8>> {
780        let file = File::open("tests/block_data.txt").unwrap();
781        let reader = BufReader::new(file);
782        let mut lines = vec![];
783        for line in reader.lines() {
784            lines.push(hex::decode(line.unwrap()).unwrap());
785        }
786        lines
787    }
788
789    fn get_test_transactions() -> (Transaction, Transaction) {
790        let block_data = read_block_data();
791        let tx1 = Block::new(&block_data[1])
792            .unwrap()
793            .transaction(0)
794            .unwrap()
795            .to_owned();
796        let tx2 = Block::new(&block_data[2])
797            .unwrap()
798            .transaction(0)
799            .unwrap()
800            .to_owned();
801        (tx1, tx2)
802    }
803
804    fn get_test_txids() -> (Txid, Txid) {
805        let (tx1, tx2) = get_test_transactions();
806        (tx1.txid().to_owned(), tx2.txid().to_owned())
807    }
808
809    fn get_test_txins() -> (TxIn, TxIn) {
810        let (tx1, tx2) = get_test_transactions();
811        (
812            tx1.as_ref().input(0).unwrap().to_owned(),
813            tx2.as_ref().input(0).unwrap().to_owned(),
814        )
815    }
816
817    fn get_test_txoutpoints() -> (TxOutPoint, TxOutPoint) {
818        let (txin1, txin2) = get_test_txins();
819        (txin1.outpoint().to_owned(), txin2.outpoint().to_owned())
820    }
821
822    test_owned_trait_requirements!(test_transaction_requirements, Transaction, btck_Transaction);
823    test_ref_trait_requirements!(
824        test_transaction_ref_requirements,
825        TransactionRef<'static>,
826        btck_Transaction
827    );
828    test_owned_clone_and_send!(
829        test_transaction_clone_send,
830        get_test_transactions().0,
831        get_test_transactions().1
832    );
833    test_ref_copy!(test_transaction_ref_behavior, get_test_transactions().0);
834
835    test_owned_trait_requirements!(test_txout_requirements, TxOut, btck_TransactionOutput);
836    test_ref_trait_requirements!(
837        test_txout_ref_requirements,
838        TxOutRef<'static>,
839        btck_TransactionOutput
840    );
841    test_owned_clone_and_send!(
842        test_txout_clone_send,
843        TxOut::new(&ScriptPubkey::new(&[0x76, 0xa9]).unwrap(), 100),
844        TxOut::new(&ScriptPubkey::new(&[0x51]).unwrap(), 200)
845    );
846    test_ref_copy!(
847        test_txout_ref_copy,
848        TxOut::new(&ScriptPubkey::new(&[0x76, 0xa9]).unwrap(), 100)
849    );
850
851    test_owned_trait_requirements!(test_txin_requirements, TxIn, btck_TransactionInput);
852    test_ref_trait_requirements!(
853        test_txin_ref_requirements,
854        TxInRef<'static>,
855        btck_TransactionInput
856    );
857    test_owned_clone_and_send!(test_txin_clone_send, get_test_txins().0, get_test_txins().1);
858    test_ref_copy!(test_txin_ref_copy, get_test_txins().0);
859
860    test_owned_trait_requirements!(
861        test_txoutpoint_requirements,
862        TxOutPoint,
863        btck_TransactionOutPoint
864    );
865    test_ref_trait_requirements!(
866        test_txoutpoint_ref_requirements,
867        TxOutPointRef<'static>,
868        btck_TransactionOutPoint
869    );
870    test_owned_clone_and_send!(
871        test_txoutpoint_clone_send,
872        get_test_txoutpoints().0,
873        get_test_txoutpoints().1
874    );
875    test_ref_copy!(test_txoutpoint_ref_copy, get_test_txoutpoints().0);
876
877    test_owned_trait_requirements!(test_txid_requirements, Txid, btck_Txid);
878    test_ref_trait_requirements!(test_txid_ref_requirements, TxidRef<'static>, btck_Txid);
879    test_owned_clone_and_send!(test_txid_clone_send, get_test_txids().0, get_test_txids().1);
880    test_ref_copy!(test_txid_ref_copy, get_test_txids().0);
881
882    #[test]
883    fn test_transaction_new() {
884        let (tx, _) = get_test_transactions();
885        let encoded = tx.consensus_encode().unwrap();
886        let new_tx = Transaction::new(&encoded);
887        assert!(new_tx.is_ok());
888    }
889
890    #[test]
891    fn test_transaction_new_invalid() {
892        let invalid_data = [0xFF; 10];
893        let tx = Transaction::new(invalid_data.as_slice());
894        assert!(tx.is_err());
895    }
896
897    #[test]
898    fn test_transaction_empty() {
899        let tx = Transaction::new([].as_slice());
900        assert!(tx.is_err());
901    }
902
903    #[test]
904    fn test_transaction_try_from() {
905        let (tx, _) = get_test_transactions();
906        let encoded = tx.consensus_encode().unwrap();
907        let new_tx = Transaction::try_from(encoded.as_slice());
908        assert!(new_tx.is_ok());
909    }
910
911    #[test]
912    fn test_transaction_output_count() {
913        let (tx, _) = get_test_transactions();
914        let count = tx.output_count();
915        assert!(count > 0);
916    }
917
918    #[test]
919    fn test_transaction_input_count() {
920        let (tx, _) = get_test_transactions();
921        let count = tx.input_count();
922        assert!(count > 0);
923    }
924
925    #[test]
926    fn test_transaction_output() {
927        let (tx, _) = get_test_transactions();
928        let output = tx.output(0);
929        assert!(output.is_ok());
930    }
931
932    #[test]
933    fn test_transaction_output_out_of_bounds() {
934        let (tx, _) = get_test_transactions();
935        let count = tx.output_count();
936        let output = tx.output(count);
937        assert!(matches!(output, Err(KernelError::OutOfBounds)));
938    }
939
940    #[test]
941    fn test_transaction_input() {
942        let (tx, _) = get_test_transactions();
943        let input = tx.input(0);
944        assert!(input.is_ok());
945    }
946
947    #[test]
948    fn test_transaction_input_out_of_bounds() {
949        let (tx, _) = get_test_transactions();
950        let count = tx.input_count();
951        let input = tx.input(count);
952        assert!(matches!(input, Err(KernelError::OutOfBounds)));
953    }
954
955    #[test]
956    fn test_transaction_txid() {
957        let (tx, _) = get_test_transactions();
958        let txid1 = tx.txid();
959        let txid2 = tx.txid();
960        assert_eq!(txid1, txid2);
961    }
962
963    #[test]
964    fn test_transaction_consensus_encode() {
965        let (tx, _) = get_test_transactions();
966        let encoded = tx.consensus_encode();
967        assert!(encoded.is_ok());
968        let encoded_bytes = encoded.unwrap();
969        assert!(!encoded_bytes.is_empty());
970    }
971
972    #[test]
973    fn test_transaction_multiple_consensus_encode() {
974        let (tx, _) = get_test_transactions();
975
976        let bytes1 = tx.consensus_encode().unwrap();
977        let bytes2 = tx.consensus_encode().unwrap();
978        let bytes3 = tx.consensus_encode().unwrap();
979
980        assert_eq!(bytes1, bytes2);
981        assert_eq!(bytes2, bytes3);
982    }
983
984    #[test]
985    fn test_transaction_try_into_vec() {
986        let (tx, _) = get_test_transactions();
987        let vec_result: Result<Vec<u8>, _> = tx.clone().try_into();
988        assert!(vec_result.is_ok());
989    }
990
991    #[test]
992    fn test_transaction_try_into_vec_ref() {
993        let (tx, _) = get_test_transactions();
994        let vec_result: Result<Vec<u8>, _> = (&tx).try_into();
995        assert!(vec_result.is_ok());
996    }
997
998    #[test]
999    fn test_transaction_as_ref() {
1000        let (tx, _) = get_test_transactions();
1001        let tx_ref = tx.as_ref();
1002
1003        assert_eq!(tx.output_count(), tx_ref.output_count());
1004        assert_eq!(tx.input_count(), tx_ref.input_count());
1005    }
1006
1007    #[test]
1008    fn test_transaction_ref_to_owned() {
1009        let (tx, _) = get_test_transactions();
1010        let tx_ref = tx.as_ref();
1011        let owned_tx = tx_ref.to_owned();
1012
1013        assert_eq!(tx.output_count(), owned_tx.output_count());
1014        assert_eq!(tx.input_count(), owned_tx.input_count());
1015    }
1016
1017    #[test]
1018    fn test_transaction_multiple_outputs() {
1019        let (tx, _) = get_test_transactions();
1020        let count = tx.output_count();
1021
1022        for i in 0..count {
1023            let output = tx.output(i);
1024            assert!(output.is_ok());
1025        }
1026    }
1027
1028    #[test]
1029    fn test_transaction_multiple_inputs() {
1030        let (tx, _) = get_test_transactions();
1031        let count = tx.input_count();
1032
1033        for i in 0..count {
1034            let input = tx.input(i);
1035            assert!(input.is_ok());
1036        }
1037    }
1038
1039    #[test]
1040    fn test_different_transactions_different_txids() {
1041        let (tx1, tx2) = get_test_transactions();
1042        assert_ne!(tx1.txid(), tx2.txid());
1043    }
1044
1045    // TxOut tests
1046    #[test]
1047    fn test_txout_new() {
1048        let script = ScriptPubkey::new([0x76, 0xa9].as_slice()).unwrap();
1049        let txout = TxOut::new(&script, 100);
1050        assert_eq!(txout.value(), 100);
1051    }
1052
1053    #[test]
1054    fn test_txout_value() {
1055        let script = ScriptPubkey::new([0x51].as_slice()).unwrap();
1056        let amount = 50000;
1057        let txout = TxOut::new(&script, amount);
1058        assert_eq!(txout.value(), amount);
1059    }
1060
1061    #[test]
1062    fn test_txout_script_pubkey() {
1063        let script_data = vec![0x76, 0xa9, 0x14];
1064        let script = ScriptPubkey::new(&script_data).unwrap();
1065        let txout = TxOut::new(&script, 100);
1066
1067        let retrieved_script = txout.script_pubkey();
1068        assert_eq!(retrieved_script.to_bytes(), script_data);
1069    }
1070
1071    #[test]
1072    fn test_txout_as_ref() {
1073        let script = ScriptPubkey::new([0x76, 0xa9].as_slice()).unwrap();
1074        let txout = TxOut::new(&script, 100);
1075        let txout_ref = txout.as_ref();
1076
1077        assert_eq!(txout.value(), txout_ref.value());
1078    }
1079
1080    #[test]
1081    fn test_txout_ref_to_owned() {
1082        let script = ScriptPubkey::new([0x76, 0xa9].as_slice()).unwrap();
1083        let txout = TxOut::new(&script, 100);
1084        let txout_ref = txout.as_ref();
1085        let owned_txout = txout_ref.to_owned();
1086
1087        assert_eq!(txout.value(), owned_txout.value());
1088    }
1089
1090    #[test]
1091    fn test_txout_zero_value() {
1092        let script = ScriptPubkey::new([0x51].as_slice()).unwrap();
1093        let txout = TxOut::new(&script, 0);
1094        assert_eq!(txout.value(), 0);
1095    }
1096
1097    #[test]
1098    fn test_txout_large_value() {
1099        let script = ScriptPubkey::new([0x51].as_slice()).unwrap();
1100        let amount = 21_000_000 * 100_000_000i64;
1101        let txout = TxOut::new(&script, amount);
1102        assert_eq!(txout.value(), amount);
1103    }
1104
1105    #[test]
1106    fn test_txout_from_transaction() {
1107        let (tx, _) = get_test_transactions();
1108        let txout = tx.output(0).unwrap();
1109
1110        assert!(txout.value() >= 0);
1111        let script_bytes = txout.script_pubkey().to_bytes();
1112        assert!(!script_bytes.is_empty());
1113    }
1114
1115    // TxIn tests
1116    #[test]
1117    fn test_txin_from_transaction() {
1118        let (tx, _) = get_test_transactions();
1119        let txin = tx.input(0).unwrap();
1120        let outpoint = txin.outpoint();
1121
1122        let _ = outpoint.index();
1123        let _ = outpoint.txid();
1124    }
1125
1126    #[test]
1127    fn test_txin_as_ref() {
1128        let (tx, _) = get_test_transactions();
1129        let txin = tx.input(0).unwrap().to_owned();
1130        let txin_ref = txin.as_ref();
1131
1132        assert_eq!(txin.outpoint().index(), txin_ref.outpoint().index());
1133    }
1134
1135    #[test]
1136    fn test_txin_ref_to_owned() {
1137        let (tx, _) = get_test_transactions();
1138        let txin = tx.input(0).unwrap().to_owned();
1139        let txin_ref = txin.as_ref();
1140        let owned_txin = txin_ref.to_owned();
1141
1142        assert_eq!(txin.outpoint().index(), owned_txin.outpoint().index());
1143    }
1144
1145    // TxOutPoint tests
1146    #[test]
1147    fn test_txoutpoint_index() {
1148        let (tx, _) = get_test_transactions();
1149        let txin = tx.input(0).unwrap();
1150        let outpoint = txin.outpoint();
1151
1152        let index = outpoint.index();
1153        assert_eq!(index, u32::MAX);
1154    }
1155
1156    #[test]
1157    fn test_txoutpoint_txid() {
1158        let (tx, _) = get_test_transactions();
1159        let txin = tx.input(0).unwrap();
1160        let outpoint = txin.outpoint();
1161
1162        let txid1 = outpoint.txid();
1163        let txid2 = outpoint.txid();
1164        assert_eq!(txid1, txid2);
1165    }
1166
1167    #[test]
1168    fn test_txoutpoint_as_ref() {
1169        let (tx, _) = get_test_transactions();
1170        let txin = tx.input(0).unwrap();
1171        let outpoint = txin.outpoint().to_owned();
1172        let outpoint_ref = outpoint.as_ref();
1173
1174        assert_eq!(outpoint.index(), outpoint_ref.index());
1175    }
1176
1177    #[test]
1178    fn test_txoutpoint_ref_to_owned() {
1179        let (tx, _) = get_test_transactions();
1180        let txin = tx.input(0).unwrap();
1181        let outpoint = txin.outpoint().to_owned();
1182        let outpoint_ref = outpoint.as_ref();
1183        let owned_outpoint = outpoint_ref.to_owned();
1184
1185        assert_eq!(outpoint.index(), owned_outpoint.index());
1186    }
1187
1188    // Txid tests
1189    #[test]
1190    fn test_txid_equality() {
1191        let (tx1, tx2) = get_test_transactions();
1192
1193        let txid1 = tx1.txid().to_owned();
1194        let txid2 = tx2.txid().to_owned();
1195        let txid1_copy = tx1.txid().to_owned();
1196
1197        assert_eq!(txid1, txid1_copy,);
1198        assert_ne!(txid1, txid2,);
1199
1200        let txid1_ref = txid1.as_ref();
1201        assert_eq!(txid1, txid1_ref,);
1202        assert_eq!(txid1_ref, txid1,);
1203
1204        let txid2_ref = txid2.as_ref();
1205        assert_eq!(txid1_ref, txid1_ref);
1206        assert_ne!(txid1_ref, txid2_ref,);
1207    }
1208
1209    #[test]
1210    fn test_txid_to_bytes() {
1211        let (tx, _) = get_test_transactions();
1212        let txid = tx.txid().to_owned();
1213        let bytes = txid.to_bytes();
1214
1215        assert_eq!(bytes.len(), 32);
1216    }
1217
1218    #[test]
1219    fn test_txid_multiple_to_bytes() {
1220        let (tx, _) = get_test_transactions();
1221        let txid = tx.txid().to_owned();
1222
1223        let bytes1 = txid.to_bytes();
1224        let bytes2 = txid.to_bytes();
1225        let bytes3 = txid.to_bytes();
1226
1227        assert_eq!(bytes1, bytes2);
1228        assert_eq!(bytes2, bytes3);
1229    }
1230
1231    #[test]
1232    fn test_txid_as_ref() {
1233        let (tx, _) = get_test_transactions();
1234        let txid = tx.txid().to_owned();
1235        let txid_ref = txid.as_ref();
1236
1237        assert_eq!(txid.to_bytes(), txid_ref.to_bytes());
1238    }
1239
1240    #[test]
1241    fn test_txid_ref_to_owned() {
1242        let (tx, _) = get_test_transactions();
1243        let txid = tx.txid().to_owned();
1244        let txid_ref = txid.as_ref();
1245        let owned_txid = txid_ref.to_owned();
1246
1247        assert_eq!(txid.to_bytes(), owned_txid.to_bytes());
1248    }
1249
1250    // Polymorphism tests
1251    #[test]
1252    fn test_transaction_polymorphism() {
1253        let (tx, _) = get_test_transactions();
1254        let tx_ref = tx.as_ref();
1255
1256        fn get_output_count(transaction: &impl TransactionExt) -> usize {
1257            transaction.output_count()
1258        }
1259
1260        let count_from_owned = get_output_count(&tx);
1261        let count_from_ref = get_output_count(&tx_ref);
1262
1263        assert_eq!(count_from_owned, count_from_ref);
1264    }
1265
1266    #[test]
1267    fn test_txout_polymorphism() {
1268        let script = ScriptPubkey::new([0x76, 0xa9].as_slice()).unwrap();
1269        let txout = TxOut::new(&script, 100);
1270        let txout_ref = txout.as_ref();
1271
1272        fn get_value(output: &impl TxOutExt) -> i64 {
1273            output.value()
1274        }
1275
1276        let value_from_owned = get_value(&txout);
1277        let value_from_ref = get_value(&txout_ref);
1278
1279        assert_eq!(value_from_owned, value_from_ref);
1280    }
1281
1282    #[test]
1283    fn test_txid_polymorphism() {
1284        let (tx, _) = get_test_transactions();
1285        let txid = tx.txid().to_owned();
1286        let txid_ref = txid.as_ref();
1287
1288        fn get_bytes(txid: &impl TxidExt) -> [u8; 32] {
1289            txid.to_bytes()
1290        }
1291
1292        let bytes_from_owned = get_bytes(&txid);
1293        let bytes_from_ref = get_bytes(&txid_ref);
1294
1295        assert_eq!(bytes_from_owned, bytes_from_ref);
1296    }
1297
1298    #[test]
1299    fn test_transaction_inputs_iterator() {
1300        let (tx, _) = get_test_transactions();
1301        let count = tx.input_count();
1302
1303        let mut iter_count = 0;
1304        for input in tx.inputs() {
1305            let _ = input.outpoint();
1306            iter_count += 1;
1307        }
1308
1309        assert_eq!(iter_count, count);
1310    }
1311
1312    #[test]
1313    fn test_transaction_outputs_iterator() {
1314        let (tx, _) = get_test_transactions();
1315        let count = tx.output_count();
1316
1317        let mut iter_count = 0;
1318        for output in tx.outputs() {
1319            let _ = output.value();
1320            iter_count += 1;
1321        }
1322
1323        assert_eq!(iter_count, count);
1324    }
1325}