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
29pub trait TransactionExt: AsPtr<btck_Transaction> {
31 fn output_count(&self) -> usize {
33 unsafe { btck_transaction_count_outputs(self.as_ptr()) as usize }
34 }
35
36 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 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 fn txid(&self) -> TxidRef<'_> {
79 let ptr = unsafe { btck_transaction_get_txid(self.as_ptr()) };
80 unsafe { TxidRef::from_ptr(ptr) }
81 }
82
83 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 fn inputs(&self) -> TxInIter<'_> {
92 TxInIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) })
93 }
94
95 fn outputs(&self) -> TxOutIter<'_> {
97 TxOutIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) })
98 }
99}
100
101pub 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
327pub trait TxOutExt: AsPtr<btck_TransactionOutput> {
329 fn value(&self) -> i64 {
331 unsafe { btck_transaction_output_get_amount(self.as_ptr()) }
332 }
333
334 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#[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 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
442pub trait TxInExt: AsPtr<btck_TransactionInput> {
444 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#[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 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
538pub trait TxOutPointExt: AsPtr<btck_TransactionOutPoint> {
540 fn index(&self) -> u32 {
544 unsafe { btck_transaction_out_point_get_index(self.as_ptr()) }
545 }
546
547 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#[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 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 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
644pub trait TxidExt: AsPtr<btck_Txid> {
646 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 #[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 #[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 #[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 #[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 #[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}