use crate::script::{LockingScript, ScriptTemplateUnlock, UnlockingScript};
#[derive(Debug, Clone)]
pub struct Utxo {
pub txid: String,
pub vout: u32,
pub satoshis: u64,
pub locking_script: LockingScript,
}
#[derive(Debug)]
pub struct TransactionInput {
pub source_transaction: Option<Box<super::Transaction>>,
pub source_txid: Option<String>,
pub source_output_index: u32,
pub unlocking_script: Option<UnlockingScript>,
pub unlocking_script_template: Option<Box<ScriptTemplateUnlock>>,
pub sequence: u32,
}
impl TransactionInput {
pub fn new(source_txid: String, source_output_index: u32) -> Self {
Self {
source_transaction: None,
source_txid: Some(source_txid),
source_output_index,
unlocking_script: None,
unlocking_script_template: None,
sequence: 0xFFFFFFFF,
}
}
pub fn with_source_transaction(tx: super::Transaction, output_index: u32) -> Self {
Self {
source_transaction: Some(Box::new(tx)),
source_txid: None,
source_output_index: output_index,
unlocking_script: None,
unlocking_script_template: None,
sequence: 0xFFFFFFFF,
}
}
pub fn get_source_txid(&self) -> crate::Result<String> {
if let Some(ref txid) = self.source_txid {
return Ok(txid.clone());
}
if let Some(ref tx) = self.source_transaction {
return Ok(tx.id());
}
Err(crate::Error::TransactionError(
"Input has neither sourceTXID nor sourceTransaction".to_string(),
))
}
pub fn get_source_txid_bytes(&self) -> crate::Result<[u8; 32]> {
let txid = self.get_source_txid()?;
let bytes = crate::primitives::from_hex(&txid)?;
if bytes.len() != 32 {
return Err(crate::Error::TransactionError(format!(
"Invalid TXID length: expected 32, got {}",
bytes.len()
)));
}
let mut result = [0u8; 32];
for (i, byte) in bytes.iter().enumerate() {
result[31 - i] = *byte;
}
Ok(result)
}
pub fn source_satoshis(&self) -> Option<u64> {
self.source_transaction
.as_ref()
.and_then(|tx| tx.outputs.get(self.source_output_index as usize))
.and_then(|out| out.satoshis)
}
pub fn source_locking_script(&self) -> Option<&LockingScript> {
self.source_transaction
.as_ref()
.and_then(|tx| tx.outputs.get(self.source_output_index as usize))
.map(|out| &out.locking_script)
}
pub fn set_unlocking_script_template(&mut self, template: ScriptTemplateUnlock) {
self.unlocking_script_template = Some(Box::new(template));
}
pub fn set_unlocking_script(&mut self, script: UnlockingScript) {
self.unlocking_script = Some(script);
}
pub fn has_source_transaction(&self) -> bool {
self.source_transaction.is_some()
}
}
impl Clone for TransactionInput {
fn clone(&self) -> Self {
Self {
source_transaction: self.source_transaction.clone(),
source_txid: self.source_txid.clone(),
source_output_index: self.source_output_index,
unlocking_script: self.unlocking_script.clone(),
unlocking_script_template: None,
sequence: self.sequence,
}
}
}
impl Default for TransactionInput {
fn default() -> Self {
Self {
source_transaction: None,
source_txid: None,
source_output_index: 0,
unlocking_script: None,
unlocking_script_template: None,
sequence: 0xFFFFFFFF,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_input() {
let txid = "abc123def456789012345678901234567890123456789012345678901234abcd".to_string();
let input = TransactionInput::new(txid.clone(), 0);
assert_eq!(input.source_txid, Some(txid));
assert_eq!(input.source_output_index, 0);
assert_eq!(input.sequence, 0xFFFFFFFF);
assert!(input.unlocking_script.is_none());
}
#[test]
fn test_get_source_txid() {
let txid = "abc123def456789012345678901234567890123456789012345678901234abcd".to_string();
let input = TransactionInput::new(txid.clone(), 0);
assert_eq!(input.get_source_txid().unwrap(), txid);
}
#[test]
fn test_get_source_txid_missing() {
let input = TransactionInput::default();
assert!(input.get_source_txid().is_err());
}
#[test]
fn test_source_satoshis_none() {
let input = TransactionInput::new("abc123".repeat(11), 0);
assert!(input.source_satoshis().is_none());
}
}