diff --git a/src/core/transaction.rs b/src/core/transaction.rs index b272e44a..e8c6fd2e 100644 --- a/src/core/transaction.rs +++ b/src/core/transaction.rs @@ -1,3 +1,146 @@ +//! Transaction data structures. +//! +//! This module provides types for working with transactions, their inputs, outputs +//! outpoints, and transaction IDs. +//! +//! # Core Types +//! +//! - [`Transaction`] - A complete transaction with inputs and outputs +//! - [`Txid`] - A 32-byte hash uniquely identifying a transaction +//! - [`TxIn`] - A transaction input that spends a previous output +//! - [`TxOut`] - A transaction output containing value and spending conditions +//! - [`TxOutPoint`] - A reference to a specific output in a previous transaction +//! +//! # Common Patterns +//! +//! ## Creating and Working with Transactions +//! +//! Transactions can be created from raw serialized data or retrieved from blocks: +//! +//! ```no_run +//! use bitcoinkernel::{prelude::*, Transaction}; +//! +//! # fn example() -> Result<(), bitcoinkernel::KernelError> { +//! let tx_data = vec![0u8; 100]; // placeholder +//! let tx = Transaction::new(&tx_data)?; +//! +//! // Get transaction ID +//! let txid = tx.txid(); +//! println!("Transaction ID: {}", txid); +//! +//! // Iterate over inputs and outputs +//! for input in tx.inputs() { +//! let outpoint = input.outpoint(); +//! println!("Spending {}:{}", outpoint.txid(), outpoint.index()); +//! } +//! +//! for output in tx.outputs() { +//! println!("Output value: {} satoshis", output.value()); +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Working with Transaction IDs +//! +//! Transaction IDs can be obtained from transactions and inspected as raw bytes +//! or as a hexadecimal string: +//! +//! ```no_run +//! # use bitcoinkernel::{prelude::*, Transaction}; +//! # fn example() -> Result<(), bitcoinkernel::KernelError> { +//! # let tx_data = vec![0u8; 100]; // placeholder +//! # let tx = Transaction::new(&tx_data)?; +//! let txid = tx.txid(); +//! +//! // Display as hex string (reversed byte order) +//! println!("Txid: {}", txid); +//! +//! // Get raw bytes (internal byte order) +//! let raw_bytes = txid.to_bytes(); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Creating Transaction Outputs +//! +//! ```no_run +//! use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; +//! +//! # fn example() -> Result<(), bitcoinkernel::KernelError> { +//! // Create a script pubkey +//! let script = ScriptPubkey::new(&[0x76, 0xa9, 0x14])?; +//! +//! // Create an output with 50,000 satoshis +//! let output = TxOut::new(&script, 50_000); +//! +//! println!("Output value: {} satoshis", output.value()); +//! println!("Script: {:?}", output.script_pubkey().to_bytes()); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Examining Transaction Inputs +//! +//! ```no_run +//! # use bitcoinkernel::{prelude::*, Transaction, KernelError}; +//! # fn example(tx: &Transaction) -> Result<(), KernelError> { +//! // Get the first input +//! let input = tx.input(0)?; +//! let outpoint = input.outpoint(); +//! +//! // Check if this is a coinbase transaction +//! if outpoint.is_null() { +//! println!("This is a coinbase transaction"); +//! } else { +//! println!("Spending output {} from transaction {}", +//! outpoint.index(), outpoint.txid()); +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Calculating Transaction Statistics +//! +//! ```no_run +//! # use bitcoinkernel::{prelude::*, Transaction}; +//! # fn example() -> Result<(), bitcoinkernel::KernelError> { +//! # let tx_data = vec![0u8; 100]; // placeholder +//! # let tx = Transaction::new(&tx_data)?; +//! // Calculate total output value +//! let total: i64 = tx.outputs().map(|out| out.value()).sum(); +//! println!("Total output value: {} satoshis", total); +//! +//! // Count outputs above a threshold +//! let large_outputs = tx.outputs() +//! .filter(|out| out.value() > 1_000_000) +//! .count(); +//! println!("Outputs > 1 BTC: {}", large_outputs); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Extension Traits +//! +//! The module defines extension traits that provide common functionality for +//! both owned and borrowed types: +//! +//! - [`TransactionExt`] - Operations on transactions +//! - [`TxidExt`] - Operations on transaction IDs +//! - [`TxInExt`] - Operations on transaction inputs +//! - [`TxOutExt`] - Operations on transaction outputs +//! - [`TxOutPointExt`] - Operations on outpoints +//! +//! These traits allow writing generic code that works with either owned or +//! borrowed types. +//! +//! # Iterators +//! +//! Iterator types are provided for traversal: +//! +//! - [`TxInIter`] - Iterates over inputs in a transaction +//! - [`TxOutIter`] - Iterates over outputs in a transaction + use std::{ ffi::c_void, fmt::{self, Debug, Display, Formatter}, @@ -31,8 +174,39 @@ use crate::{ use super::script::ScriptPubkeyRef; /// Common operations for transactions, implemented by both owned and borrowed types. +/// +/// This trait provides shared functionality for [`Transaction`] and [`TransactionRef`], +/// allowing code to work with either owned or borrowed transactions. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// fn count_outputs(tx: &T) -> usize { +/// tx.output_count() +/// } +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// # let tx_data = vec![0u8; 100]; +/// let tx = Transaction::new(&tx_data)?; +/// let count = count_outputs(&tx); +/// # Ok(()) +/// # } +/// ``` pub trait TransactionExt: AsPtr { /// Returns the number of outputs in this transaction. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// println!("Transaction has {} outputs", tx.output_count()); + /// # Ok(()) + /// # } + /// ``` fn output_count(&self) -> usize { unsafe { btck_transaction_count_outputs(self.as_ptr()) as usize } } @@ -43,8 +217,20 @@ pub trait TransactionExt: AsPtr { /// * `index` - The zero-based index of the output to retrieve /// /// # Returns - /// * `Ok(RefType)` - A reference to the output + /// * `Ok([`TxOutRef`])` - A reference to the output /// * `Err(KernelError::OutOfBounds)` - If the index is invalid + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example() -> Result<(), KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let first_output = tx.output(0)?; + /// println!("First output value: {} satoshis", first_output.value()); + /// # Ok(()) + /// # } + /// ``` fn output(&self, index: usize) -> Result, KernelError> { if index >= self.output_count() { return Err(KernelError::OutOfBounds); @@ -56,6 +242,18 @@ pub trait TransactionExt: AsPtr { Ok(tx_out_ref) } + /// Returns the number of inputs in this transaction. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// println!("Transaction has {} inputs", tx.input_count()); + /// # Ok(()) + /// # } + /// ``` fn input_count(&self) -> usize { unsafe { btck_transaction_count_inputs(self.as_ptr()) as usize } } @@ -66,8 +264,22 @@ pub trait TransactionExt: AsPtr { /// * `index` - The zero-based index of the input to retrieve /// /// # Returns - /// * `Ok(TxInRef)` - A reference to the input + /// * `Ok([`TxInRef`])` - A reference to the input /// * `Err(KernelError::OutOfBounds)` - If the index is invalid + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example() -> Result<(), KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let first_input = tx.input(0)?; + /// let outpoint = first_input.outpoint(); + /// println!("Spending output {} from transaction {}", + /// outpoint.index(), outpoint.txid()); + /// # Ok(()) + /// # } + /// ``` fn input(&self, index: usize) -> Result, KernelError> { if index >= self.input_count() { return Err(KernelError::OutOfBounds); @@ -79,12 +291,45 @@ pub trait TransactionExt: AsPtr { } /// Returns a reference to the transaction ID (txid) of this transaction. + /// + /// The txid is the double SHA256 hash of the serialized transaction and serves + /// as the transaction's unique identifier. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let txid = tx.txid(); + /// println!("Transaction ID: {}", txid); + /// # Ok(()) + /// # } + /// ``` fn txid(&self) -> TxidRef<'_> { let ptr = unsafe { btck_transaction_get_txid(self.as_ptr()) }; unsafe { TxidRef::from_ptr(ptr) } } - /// Consensus encodes the transaction to Bitcoin wire format. + /// Serializes the transaction to Bitcoin wire format. + /// + /// Encodes the complete transaction according to Bitcoin consensus rules. + /// The resulting data can be transmitted over the network or stored to disk. + /// + /// # Errors + /// Returns [`KernelError::Internal`] if serialization fails. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example() -> Result<(), KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let serialized = tx.consensus_encode()?; + /// println!("Transaction is {} bytes", serialized.len()); + /// # Ok(()) + /// # } + /// ``` fn consensus_encode(&self) -> Result, KernelError> { c_serialize(|callback, user_data| unsafe { btck_transaction_to_bytes(self.as_ptr(), Some(callback), user_data) @@ -92,17 +337,81 @@ pub trait TransactionExt: AsPtr { } /// Returns an iterator over all inputs in this transaction. + /// + /// The iterator yields [`TxInRef`] instances in the order they appear in the + /// transaction. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// for (i, input) in tx.inputs().enumerate() { + /// let outpoint = input.outpoint(); + /// println!("Input {}: spending output {} from {}", + /// i, outpoint.index(), outpoint.txid()); + /// } + /// # Ok(()) + /// # } + /// ``` fn inputs(&self) -> TxInIter<'_> { TxInIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) }) } /// Returns an iterator over all outputs in this transaction. + /// + /// The iterator yields [`TxOutRef`] instances in the order they appear in the + /// transaction. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// for (i, output) in tx.outputs().enumerate() { + /// println!("Output {}: {} satoshis", i, output.value()); + /// } + /// # Ok(()) + /// # } + /// ``` fn outputs(&self) -> TxOutIter<'_> { TxOutIter::new(unsafe { TransactionRef::from_ptr(self.as_ptr()) }) } } /// A Bitcoin transaction. +/// +/// # Creation +/// +/// Transactions are created from: +/// - Raw serialized transaction data using [`new`](Self::new) +/// - Read from Blocks via [`Block::transaction`](crate::Block::transaction) +/// +/// # Thread Safety +/// +/// `Transaction` is both [`Send`] and [`Sync`], allowing it to be safely shared +/// across threads or moved between threads. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// let tx_data = vec![0u8; 100]; // placeholder +/// let tx = Transaction::new(&tx_data)?; +/// +/// println!("Transaction ID: {}", tx.txid()); +/// println!("Inputs: {}, Outputs: {}", tx.input_count(), tx.output_count()); +/// +/// // Calculate total output value +/// let total: i64 = tx.outputs().map(|out| out.value()).sum(); +/// println!("Total output value: {} satoshis", total); +/// # Ok(()) +/// # } +/// ``` pub struct Transaction { inner: *mut btck_Transaction, } @@ -111,6 +420,29 @@ unsafe impl Send for Transaction {} unsafe impl Sync for Transaction {} impl Transaction { + /// Creates a new transaction from raw serialized data. + /// + /// Deserializes a transaction from its wire format representation. + /// + /// # Arguments + /// * `transaction_bytes` - The serialized transaction data in Bitcoin wire format + /// + /// # Errors + /// Returns [`KernelError::Internal`] if: + /// - The data is not a valid transaction + /// - The data is incomplete + /// - Deserialization fails + /// + /// # Examples + /// ```no_run + /// use bitcoinkernel::Transaction; + /// + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// let tx_data = vec![0u8; 100]; // placeholder + /// let tx = Transaction::new(&tx_data)?; + /// # Ok(()) + /// # } + /// ``` pub fn new(transaction_bytes: &[u8]) -> Result { let inner = unsafe { btck_transaction_create( @@ -128,6 +460,25 @@ impl Transaction { } } + /// Creates a borrowed reference to this transaction. + /// + /// This allows converting from an owned [`Transaction`] to a [`TransactionRef`] + /// without copying the underlying data. + /// + /// # Lifetime + /// The returned reference is valid for the lifetime of this [`Transaction`]. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let tx_ref = tx.as_ref(); + /// assert_eq!(tx.input_count(), tx_ref.input_count()); + /// # Ok(()) + /// # } + /// ``` pub fn as_ref(&self) -> TransactionRef<'_> { unsafe { TransactionRef::from_ptr(self.inner as *const _) } } @@ -185,6 +536,16 @@ impl TryFrom<&Transaction> for Vec { } } +/// A borrowed reference to a transaction. +/// +/// Provides zero-copy access to transaction data. It implements [`Copy`], +/// making it cheap to pass around. +/// +/// # Lifetime +/// The reference is only valid as long as the data it references remains alive. +/// +/// # Thread Safety +/// `TransactionRef` is both [`Send`] and [`Sync`]. pub struct TransactionRef<'a> { inner: *const btck_Transaction, marker: PhantomData<&'a ()>, @@ -194,6 +555,22 @@ unsafe impl<'a> Send for TransactionRef<'a> {} unsafe impl<'a> Sync for TransactionRef<'a> {} impl<'a> TransactionRef<'a> { + /// Creates an owned copy of this transaction. + /// + /// This allocates a new [`Transaction`] with its own copy of the transaction data. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let tx_ref = tx.as_ref(); + /// let owned = tx_ref.to_owned(); + /// assert_eq!(tx.txid(), owned.txid()); + /// # Ok(()) + /// # } + /// ``` pub fn to_owned(&self) -> Transaction { Transaction { inner: unsafe { btck_transaction_copy(self.inner) }, @@ -225,6 +602,38 @@ impl<'a> Clone for TransactionRef<'a> { impl<'a> Copy for TransactionRef<'a> {} +/// Iterator over transaction inputs. +/// +/// This iterator yields [`TxInRef`] items for each input in the transaction, +/// in the order they appear in the transaction. +/// +/// # Lifetime +/// The iterator is tied to the lifetime of the [`Transaction`] it was created from. +/// The iterator becomes invalid when the transaction is dropped. +/// +/// # Examples +/// ```no_run +/// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; +/// # fn example() -> Result<(), KernelError> { +/// # let tx_data = vec![0u8; 100]; // placeholder +/// # let tx = Transaction::new(&tx_data)?; +/// // Iterate through all inputs +/// for input in tx.inputs() { +/// let outpoint = input.outpoint(); +/// println!("Spending: {}:{}", outpoint.txid(), outpoint.index()); +/// } +/// +/// // Or with enumerate for explicit indexing +/// for (idx, input) in tx.inputs().enumerate() { +/// println!("Input {}: {}", idx, input.outpoint().txid()); +/// } +/// +/// // Use iterator adapters +/// let input_count = tx.inputs().count(); +/// println!("Transaction has {} inputs", input_count); +/// # Ok(()) +/// # } +/// ``` pub struct TxInIter<'a> { transaction: TransactionRef<'a>, current_index: usize, @@ -276,6 +685,39 @@ impl<'a> ExactSizeIterator for TxInIter<'a> { } } +/// Iterator over transaction outputs. +/// +/// This iterator yields [`TxOutRef`] items for each output in the transaction, +/// in the order they appear in the transaction. +/// +/// # Lifetime +/// The iterator is tied to the lifetime of the [`Transaction`] it was created from. +/// The iterator becomes invalid when the transaction is dropped. +/// +/// # Examples +/// ```no_run +/// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; +/// # fn example() -> Result<(), KernelError> { +/// # let tx_data = vec![0u8; 100]; // placeholder +/// # let tx = Transaction::new(&tx_data)?; +/// // Iterate through all outputs +/// for output in tx.outputs() { +/// println!("Output value: {} satoshis", output.value()); +/// } +/// +/// // Calculate total output value +/// let total: i64 = tx.outputs() +/// .map(|out| out.value()) +/// .sum(); +/// println!("Total: {} satoshis", total); +/// +/// // Find outputs above a threshold +/// let large_outputs: Vec<_> = tx.outputs() +/// .filter(|out| out.value() > 1_000_000) +/// .collect(); +/// # Ok(()) +/// # } +/// ``` pub struct TxOutIter<'a> { transaction: TransactionRef<'a>, current_index: usize, @@ -329,16 +771,56 @@ impl<'a> ExactSizeIterator for TxOutIter<'a> { } /// Common operations for transaction outputs, implemented by both owned and borrowed types. +/// +/// This trait provides shared functionality for [`TxOut`] and [`TxOutRef`], +/// allowing code to work with either owned or borrowed outputs. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; +/// +/// fn print_value(output: &T) { +/// println!("Output value: {} satoshis", output.value()); +/// } +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// let script = ScriptPubkey::new(&[0x76, 0xa9])?; +/// let output = TxOut::new(&script, 50000); +/// print_value(&output); +/// # Ok(()) +/// # } +/// ``` pub trait TxOutExt: AsPtr { /// Returns the amount of this output in satoshis. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let script = ScriptPubkey::new(&[0x76, 0xa9])?; + /// let output = TxOut::new(&script, 50000); + /// assert_eq!(output.value(), 50000); + /// # Ok(()) + /// # } + /// ``` fn value(&self) -> i64 { unsafe { btck_transaction_output_get_amount(self.as_ptr()) } } /// Returns a reference to the script pubkey that defines how this output can be spent. /// - /// # Returns - /// * `RefType` - A reference to the script pubkey + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let script = ScriptPubkey::new(&[0x76, 0xa9])?; + /// let output = TxOut::new(&script, 50000); + /// let script_bytes = output.script_pubkey().to_bytes(); + /// println!("Script is {} bytes", script_bytes.len()); + /// # Ok(()) + /// # } + /// ``` fn script_pubkey(&self) -> ScriptPubkeyRef<'_> { let ptr = unsafe { btck_transaction_output_get_script_pubkey(self.as_ptr()) }; unsafe { ScriptPubkeyRef::from_ptr(ptr) } @@ -349,6 +831,26 @@ pub trait TxOutExt: AsPtr { /// /// Transaction outputs can be created from a script pubkey and amount, or retrieved /// from existing transactions. They represent spendable coins in the UTXO set. +/// +/// # Thread Safety +/// +/// `TxOut` is both [`Send`] and [`Sync`]. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// // Create a new output +/// let script = ScriptPubkey::new(&[0x76, 0xa9, 0x14])?; +/// let output = TxOut::new(&script, 50000); +/// +/// println!("Value: {} satoshis", output.value()); +/// println!("Script: {:?}", output.script_pubkey().to_bytes()); +/// # Ok(()) +/// # } +/// ``` #[derive(Debug)] pub struct TxOut { inner: *mut btck_TransactionOutput, @@ -363,12 +865,30 @@ impl TxOut { /// # Arguments /// * `script_pubkey` - The script defining how this output can be spent /// * `amount` - The amount in satoshis + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, TxOut, ScriptPubkey}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// let script = ScriptPubkey::new(&[0x76, 0xa9])?; + /// let output = TxOut::new(&script, 50000); + /// assert_eq!(output.value(), 50000); + /// # Ok(()) + /// # } + /// ``` pub fn new(script_pubkey: &impl ScriptPubkeyExt, amount: i64) -> Self { TxOut { inner: unsafe { btck_transaction_output_create(script_pubkey.as_ptr(), amount) }, } } + /// Creates a borrowed reference to this output. + /// + /// This allows converting from an owned [`TxOut`] to a [`TxOutRef`] + /// without copying the underlying data. + /// + /// # Lifetime + /// The returned reference is valid for the lifetime of this [`TxOut`]. pub fn as_ref(&self) -> TxOutRef<'_> { unsafe { TxOutRef::from_ptr(self.inner as *const _) } } @@ -402,6 +922,16 @@ impl Drop for TxOut { } } +/// A borrowed reference to a transaction output. +/// +/// Provides zero-copy access to output data. It implements [`Copy`], +/// making it cheap to pass around. +/// +/// # Lifetime +/// The reference is valid only as long as the data it references remains alive. +/// +/// # Thread Safety +/// `TxOutRef` is both [`Send`] and [`Sync`]. pub struct TxOutRef<'a> { inner: *const btck_TransactionOutput, marker: PhantomData<&'a ()>, @@ -411,6 +941,9 @@ unsafe impl<'a> Send for TxOutRef<'a> {} unsafe impl<'a> Sync for TxOutRef<'a> {} impl<'a> TxOutRef<'a> { + /// Creates an owned copy of this output. + /// + /// This allocates a new [`TxOut`] with its own copy of the output data. pub fn to_owned(&self) -> TxOut { TxOut { inner: unsafe { btck_transaction_output_copy(self.inner) }, @@ -444,10 +977,39 @@ impl<'a> Clone for TxOutRef<'a> { impl<'a> Copy for TxOutRef<'a> {} /// Common operations for transaction inputs, implemented by both owned and borrowed types. +/// +/// This trait provides shared functionality for [`TxIn`] and [`TxInRef`], +/// allowing code to work with either owned or borrowed inputs. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// fn print_outpoint(input: &T) { +/// let outpoint = input.outpoint(); +/// println!("Spending {}:{}", outpoint.txid(), outpoint.index()); +/// } +/// ``` pub trait TxInExt: AsPtr { /// Returns a reference to the outpoint being spent by this input. /// - /// The outpoint identifies which previous transaction output this input is spending. + /// The outpoint identifies which previous transaction output this input is spending + /// by referencing the transaction ID and output index. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example(tx: &Transaction) -> Result<(), KernelError> { + /// let input = tx.input(0)?; + /// let outpoint = input.outpoint(); + /// + /// println!("Spending output {} from transaction {}", + /// outpoint.index(), + /// outpoint.txid()); + /// # Ok(()) + /// # } + /// ``` fn outpoint(&self) -> TxOutPointRef<'_> { let ptr = unsafe { btck_transaction_input_get_out_point(self.as_ptr()) }; unsafe { TxOutPointRef::from_ptr(ptr) } @@ -455,6 +1017,10 @@ pub trait TxInExt: AsPtr { } /// A single transaction input referencing a previous output to be spent. +/// +/// # Thread Safety +/// +/// `TxIn` is both [`Send`] and [`Sync`]. #[derive(Debug)] pub struct TxIn { inner: *mut btck_TransactionInput, @@ -464,6 +1030,13 @@ unsafe impl Send for TxIn {} unsafe impl Sync for TxIn {} impl TxIn { + /// Creates a borrowed reference to this input. + /// + /// This allows converting from an owned [`TxIn`] to a [`TxInRef`] + /// without copying the underlying data. + /// + /// # Lifetime + /// The returned reference is valid for the lifetime of this [`TxIn`]. pub fn as_ref(&self) -> TxInRef<'_> { unsafe { TxInRef::from_ptr(self.inner as *const _) } } @@ -497,6 +1070,16 @@ impl Drop for TxIn { } } +/// A borrowed reference to a transaction input. +/// +/// Provides zero-copy access to input data. It implements [`Copy`], +/// making it cheap to pass around. +/// +/// # Lifetime +/// The reference is valid only as long as the data it references remains alive. +/// +/// # Thread Safety +/// `TxInRef` is both [`Send`] and [`Sync`]. pub struct TxInRef<'a> { inner: *const btck_TransactionInput, marker: PhantomData<&'a ()>, @@ -507,6 +1090,8 @@ unsafe impl<'a> Sync for TxInRef<'a> {} impl<'a> TxInRef<'a> { /// Creates an owned copy of this transaction input. + /// + /// This allocates a new [`TxIn`] with its own copy of the input data. pub fn to_owned(&self) -> TxIn { TxIn { inner: unsafe { btck_transaction_input_copy(self.inner) }, @@ -539,22 +1124,83 @@ impl<'a> Clone for TxInRef<'a> { impl<'a> Copy for TxInRef<'a> {} -/// Common operations for transaction out points, implemented by both owned and borrowed types. +/// Common operations for transaction outpoints, implemented by both owned and borrowed types. +/// +/// This trait provides shared functionality for [`TxOutPoint`] and [`TxOutPointRef`], +/// allowing code to work with either owned or borrowed outpoints. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// fn is_coinbase(outpoint: &T) -> bool { +/// outpoint.is_null() +/// } +/// ``` pub trait TxOutPointExt: AsPtr { /// Returns the output index within the referenced transaction. /// /// This is the zero-based index of the output in the transaction's output list. + /// + /// # Special Value + /// Returns `u32::MAX` (0xFFFFFFFF) for coinbase transactions, indicating a null outpoint. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example(tx: &Transaction) -> Result<(), KernelError> { + /// let input = tx.input(0)?; + /// let outpoint = input.outpoint(); + /// println!("Spending output at index {}", outpoint.index()); + /// # Ok(()) + /// # } + /// ``` fn index(&self) -> u32 { unsafe { btck_transaction_out_point_get_index(self.as_ptr()) } } /// Returns a reference to the transaction ID of the transaction containing this output. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example(tx: &Transaction) -> Result<(), KernelError> { + /// let input = tx.input(0)?; + /// let outpoint = input.outpoint(); + /// println!("Previous transaction: {}", outpoint.txid()); + /// # Ok(()) + /// # } + /// ``` fn txid(&self) -> TxidRef<'_> { let ptr = unsafe { btck_transaction_out_point_get_txid(self.as_ptr()) }; unsafe { TxidRef::from_ptr(ptr) } } - /// Returns true if this OutPoint is the "null" coinbase OutPoint. + /// Returns true if this outpoint is the "null" coinbase outpoint. + /// + /// A null outpoint has: + /// - Index: `u32::MAX` (0xFFFFFFFF) + /// - Txid: All zeros + /// + /// This indicates a coinbase transaction input, which creates new coins rather + /// than spending existing outputs. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example(tx: &Transaction) -> Result<(), KernelError> { + /// let input = tx.input(0)?; + /// let outpoint = input.outpoint(); + /// + /// if outpoint.is_null() { + /// println!("This is a coinbase transaction"); + /// } else { + /// println!("This transaction spends existing outputs"); + /// } + /// # Ok(()) + /// # } + /// ``` fn is_null(&self) -> bool { self.index() == u32::MAX && self.txid().is_all_zeros() } @@ -565,6 +1211,41 @@ pub trait TxOutPointExt: AsPtr { /// An outpoint uniquely identifies a transaction output by combining a transaction ID /// with an output index. Outpoints are used in transaction inputs to specify which /// previous outputs are being spent. +/// +/// # Structure +/// +/// Each outpoint contains: +/// - **Txid**: The transaction ID (32-byte hash) +/// - **Index**: The zero-based index of the output in that transaction +/// +/// # Special Case: Coinbase +/// +/// Coinbase transaction inputs use a "null" outpoint where the txid is all zeros and +/// the index is `u32::MAX`. Use [`is_null`](TxOutPointExt::is_null) to check for this. +/// +/// # Thread Safety +/// +/// `TxOutPoint` is both [`Send`] and [`Sync`]. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// # let tx_data = vec![0u8; 100]; // placeholder +/// # let tx = Transaction::new(&tx_data)?; +/// let input = tx.input(0)?; +/// let outpoint = input.outpoint(); +/// +/// if outpoint.is_null() { +/// println!("Coinbase transaction"); +/// } else { +/// println!("Spending {}:{}", outpoint.txid(), outpoint.index()); +/// } +/// # Ok(()) +/// # } +/// ``` #[derive(Debug)] pub struct TxOutPoint { inner: *mut btck_TransactionOutPoint, @@ -575,6 +1256,12 @@ unsafe impl Sync for TxOutPoint {} impl TxOutPoint { /// Returns a borrowed reference to this outpoint. + /// + /// This allows converting from an owned [`TxOutPoint`] to a [`TxOutPointRef`] + /// without copying the underlying data. + /// + /// # Lifetime + /// The returned reference is valid for the lifetime of this [`TxOutPoint`]. pub fn as_ref(&self) -> TxOutPointRef<'_> { unsafe { TxOutPointRef::from_ptr(self.inner as *const _) } } @@ -608,6 +1295,16 @@ impl Drop for TxOutPoint { } } +/// A borrowed reference to an outpoint. +/// +/// Provides zero-copy access to outpoint data. It implements [`Copy`], +/// making it cheap to pass around. +/// +/// # Lifetime +/// The reference is valid only as long as the data it references remains alive. +/// +/// # Thread Safety +/// `TxOutPointRef` is both [`Send`] and [`Sync`]. pub struct TxOutPointRef<'a> { inner: *const btck_TransactionOutPoint, marker: PhantomData<&'a ()>, @@ -618,6 +1315,8 @@ unsafe impl<'a> Sync for TxOutPointRef<'a> {} impl<'a> TxOutPointRef<'a> { /// Creates an owned copy of this outpoint. + /// + /// This allocates a new [`TxOutPoint`] with its own copy of the outpoint data. pub fn to_owned(&self) -> TxOutPoint { TxOutPoint { inner: unsafe { btck_transaction_out_point_copy(self.inner) }, @@ -651,8 +1350,42 @@ impl<'a> Clone for TxOutPointRef<'a> { impl<'a> Copy for TxOutPointRef<'a> {} /// Common operations for transaction IDs, implemented by both owned and borrowed types. +/// +/// This trait provides shared functionality for [`Txid`] and [`TxidRef`], +/// allowing code to work with either owned or borrowed transaction IDs. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// fn print_txid(txid: &T) { +/// println!("Transaction ID: {}", txid); +/// } +/// ``` pub trait TxidExt: AsPtr + Display { /// Serializes the txid to raw bytes. + /// + /// Returns the 32-byte representation of the transaction ID in internal byte order. + /// + /// # Byte Order + /// The bytes are in internal order. + /// + /// # Returns + /// A 32-byte array containing the transaction ID. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction}; + /// # fn example() -> Result<(), bitcoinkernel::KernelError> { + /// # let tx_data = vec![0u8; 100]; // placeholder + /// # let tx = Transaction::new(&tx_data)?; + /// let txid = tx.txid(); + /// let bytes = txid.to_bytes(); + /// assert_eq!(bytes.len(), 32); + /// # Ok(()) + /// # } + /// ``` fn to_bytes(&self) -> [u8; 32] { let mut bytes = [0u8; 32]; unsafe { @@ -662,11 +1395,66 @@ pub trait TxidExt: AsPtr + Display { } /// Returns true if all bytes of the txid are zero (null txid). + /// + /// A null txid (all zeros) is used in coinbase transaction outpoints to indicate + /// that no previous output is being spent. + /// + /// # Examples + /// ```no_run + /// # use bitcoinkernel::{prelude::*, Transaction, KernelError}; + /// # fn example(tx: &Transaction) -> Result<(), KernelError> { + /// let input = tx.input(0)?; + /// let outpoint = input.outpoint(); + /// let txid = outpoint.txid(); + /// + /// if txid.is_all_zeros() { + /// println!("This is a null txid (coinbase)"); + /// } + /// # Ok(()) + /// # } + /// ``` fn is_all_zeros(&self) -> bool { self.to_bytes().iter().all(|&b| b == 0) } } +/// A 32-byte hash uniquely identifying a transaction. +/// +/// Transaction IDs are the double SHA256 hash of the serialized transaction and +/// serve as the transaction's unique identifier. +/// +/// # Byte Order +/// +/// Bitcoin uses two different representations of transaction IDs: +/// - **Internal byte order**: Used in memory, on disk, and for hashing +/// - **Display byte order**: Reversed for human-readable hex strings +/// +/// The [`to_bytes`](TxidExt::to_bytes) method returns internal byte order, +/// while [`Display`](std::fmt::Display) formatting shows the reversed bytes. +/// +/// # Thread Safety +/// +/// `Txid` is both [`Send`] and [`Sync`], allowing it to be safely +/// shared across threads. +/// +/// # Examples +/// +/// ```no_run +/// use bitcoinkernel::{prelude::*, Transaction}; +/// +/// # fn example() -> Result<(), bitcoinkernel::KernelError> { +/// # let tx_data = vec![0u8; 100]; // placeholder +/// let tx = Transaction::new(&tx_data)?; +/// let txid = tx.txid().to_owned(); +/// +/// // Display as hex (reversed byte order) +/// println!("Transaction: {}", txid); +/// +/// // Get internal representation +/// let bytes = txid.to_bytes(); +/// # Ok(()) +/// # } +/// ``` pub struct Txid { inner: *mut btck_Txid, } @@ -675,6 +1463,13 @@ unsafe impl Send for Txid {} unsafe impl Sync for Txid {} impl Txid { + /// Creates a borrowed reference to this transaction ID. + /// + /// This allows converting from an owned [`Txid`] to a [`TxidRef`] + /// without copying the underlying data. + /// + /// # Lifetime + /// The returned reference is valid for the lifetime of this [`Txid`]. pub fn as_ref(&self) -> TxidRef<'_> { unsafe { TxidRef::from_ptr(self.inner as *const _) } } @@ -738,6 +1533,16 @@ impl Display for Txid { } } +/// A borrowed reference to a transaction ID. +/// +/// Provides zero-copy access to transaction ID data. It implements [`Copy`], +/// making it cheap to pass around. +/// +/// # Lifetime +/// The reference is valid only as long as the data it references remains alive. +/// +/// # Thread Safety +/// `TxidRef` is both [`Send`] and [`Sync`]. pub struct TxidRef<'a> { inner: *const btck_Txid, marker: PhantomData<&'a ()>, @@ -747,6 +1552,9 @@ unsafe impl<'a> Send for TxidRef<'a> {} unsafe impl<'a> Sync for TxidRef<'a> {} impl<'a> TxidRef<'a> { + /// Creates an owned copy of this transaction ID. + /// + /// This allocates a new [`Txid`] with its own copy of the ID data. pub fn to_owned(&self) -> Txid { Txid { inner: unsafe { btck_txid_copy(self.inner) },