bsv_rs/script/transaction.rs
1//! Transaction interface traits for Script module integration.
2//!
3//! This module defines traits that the future Transaction module will implement
4//! to integrate with the Script module for spend validation.
5//!
6//! The traits provide a clean abstraction layer that allows the Script module
7//! to work with transaction data without depending on specific Transaction
8//! implementations.
9//!
10//! # Example Implementation
11//!
12//! ```rust,ignore
13//! use bsv_rs::script::transaction::{TransactionInputContext, TransactionOutputContext};
14//!
15//! impl TransactionInputContext for TransactionInput {
16//! fn source_txid(&self) -> &[u8; 32] { &self.prev_txid }
17//! fn source_output_index(&self) -> u32 { self.prev_output_index }
18//! fn sequence(&self) -> u32 { self.sequence }
19//! fn source_satoshis(&self) -> Option<u64> { self.source_satoshis }
20//! fn source_locking_script(&self) -> Option<&LockingScript> { self.source_script.as_ref() }
21//! }
22//! ```
23
24use super::{LockingScript, UnlockingScript};
25
26// ============================================================================
27// Transaction Input Context
28// ============================================================================
29
30/// Context from a transaction input needed for script validation.
31///
32/// This trait abstracts the transaction input data needed by the Script module
33/// to validate spends. The Transaction module will implement this trait.
34pub trait TransactionInputContext {
35 /// The transaction ID of the UTXO being spent (32 bytes, internal byte order).
36 fn source_txid(&self) -> &[u8; 32];
37
38 /// The index of the output being spent in the source transaction.
39 fn source_output_index(&self) -> u32;
40
41 /// The sequence number of this input.
42 fn sequence(&self) -> u32;
43
44 /// The satoshi value of the UTXO being spent.
45 ///
46 /// Returns `None` if the source transaction data is not available.
47 /// This is required for sighash computation (BIP-143).
48 fn source_satoshis(&self) -> Option<u64>;
49
50 /// The locking script of the UTXO being spent.
51 ///
52 /// Returns `None` if the source transaction data is not available.
53 fn source_locking_script(&self) -> Option<&LockingScript>;
54
55 /// The unlocking script for this input.
56 fn unlocking_script(&self) -> &UnlockingScript;
57}
58
59// ============================================================================
60// Transaction Output Context
61// ============================================================================
62
63/// Context from a transaction output.
64///
65/// This trait abstracts transaction output data. The Transaction module
66/// will implement this trait.
67pub trait TransactionOutputContext {
68 /// The satoshi value of this output.
69 fn satoshis(&self) -> u64;
70
71 /// The locking script of this output.
72 fn locking_script(&self) -> &LockingScript;
73}
74
75// ============================================================================
76// Transaction Context
77// ============================================================================
78
79/// Full transaction context for script validation.
80///
81/// This trait provides all the data needed by the Spend validator to
82/// validate a transaction input. The Transaction module will implement
83/// this trait.
84pub trait TransactionContext {
85 /// The input type implementing TransactionInputContext.
86 type Input: TransactionInputContext;
87
88 /// The output type implementing TransactionOutputContext.
89 type Output: TransactionOutputContext;
90
91 /// Transaction version.
92 fn version(&self) -> i32;
93
94 /// All inputs in this transaction.
95 fn inputs(&self) -> &[Self::Input];
96
97 /// All outputs in this transaction.
98 fn outputs(&self) -> &[Self::Output];
99
100 /// The lock time.
101 fn lock_time(&self) -> u32;
102
103 /// Get a specific input by index.
104 fn input(&self, index: usize) -> Option<&Self::Input> {
105 self.inputs().get(index)
106 }
107
108 /// Get a specific output by index.
109 fn output(&self, index: usize) -> Option<&Self::Output> {
110 self.outputs().get(index)
111 }
112
113 /// Number of inputs.
114 fn input_count(&self) -> usize {
115 self.inputs().len()
116 }
117
118 /// Number of outputs.
119 fn output_count(&self) -> usize {
120 self.outputs().len()
121 }
122}
123
124// ============================================================================
125// Spend Validation Extension
126// ============================================================================
127
128/// Extension trait for validating all inputs of a transaction.
129///
130/// This trait provides convenient methods for validating transaction spends
131/// when the transaction implements `TransactionContext`.
132pub trait SpendValidation: TransactionContext {
133 /// Validate a specific input by index.
134 ///
135 /// Returns `Ok(true)` if the input is valid, `Ok(false)` if invalid,
136 /// or `Err` with details if validation fails with an error.
137 fn validate_input(
138 &self,
139 index: usize,
140 ) -> Result<bool, Box<crate::script::ScriptEvaluationError>>;
141
142 /// Validate all inputs in the transaction.
143 ///
144 /// Returns `Ok(())` if all inputs are valid, or the first error encountered.
145 fn validate_all_inputs(&self) -> Result<(), Box<crate::script::ScriptEvaluationError>> {
146 for i in 0..self.input_count() {
147 match self.validate_input(i) {
148 Ok(true) => continue,
149 Ok(false) => {
150 return Err(Box::new(crate::script::ScriptEvaluationError {
151 message: format!("Input {} validation returned false", i),
152 source_txid: String::new(),
153 source_output_index: 0,
154 context: crate::script::ExecutionContext::UnlockingScript,
155 program_counter: 0,
156 stack: vec![],
157 alt_stack: vec![],
158 if_stack: vec![],
159 stack_mem: 0,
160 alt_stack_mem: 0,
161 resource_limit: None,
162 }));
163 }
164 Err(e) => return Err(e),
165 }
166 }
167 Ok(())
168 }
169}
170
171// ============================================================================
172// UTXO Provider
173// ============================================================================
174
175/// Trait for looking up UTXOs for spend validation.
176///
177/// Transaction validation requires knowing the locking scripts and values
178/// of the UTXOs being spent. This trait abstracts UTXO lookup.
179pub trait UtxoProvider {
180 /// Look up a UTXO by transaction ID and output index.
181 ///
182 /// Returns the satoshi value and locking script if found.
183 fn get_utxo(&self, txid: &[u8; 32], output_index: u32) -> Option<(u64, LockingScript)>;
184}
185
186// ============================================================================
187// Simple Implementations for Testing
188// ============================================================================
189
190/// A simple UTXO for testing purposes.
191#[derive(Debug, Clone)]
192pub struct SimpleUtxo {
193 /// Satoshi value.
194 pub satoshis: u64,
195 /// Locking script.
196 pub locking_script: LockingScript,
197}
198
199impl TransactionOutputContext for SimpleUtxo {
200 fn satoshis(&self) -> u64 {
201 self.satoshis
202 }
203
204 fn locking_script(&self) -> &LockingScript {
205 &self.locking_script
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn test_simple_utxo() {
215 let utxo = SimpleUtxo {
216 satoshis: 100_000,
217 locking_script: LockingScript::from_asm("OP_DUP OP_HASH160").unwrap(),
218 };
219
220 assert_eq!(utxo.satoshis(), 100_000);
221 assert!(utxo.locking_script().to_asm().contains("OP_DUP"));
222 }
223}