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 }));
162 }
163 Err(e) => return Err(e),
164 }
165 }
166 Ok(())
167 }
168}
169
170// ============================================================================
171// UTXO Provider
172// ============================================================================
173
174/// Trait for looking up UTXOs for spend validation.
175///
176/// Transaction validation requires knowing the locking scripts and values
177/// of the UTXOs being spent. This trait abstracts UTXO lookup.
178pub trait UtxoProvider {
179 /// Look up a UTXO by transaction ID and output index.
180 ///
181 /// Returns the satoshi value and locking script if found.
182 fn get_utxo(&self, txid: &[u8; 32], output_index: u32) -> Option<(u64, LockingScript)>;
183}
184
185// ============================================================================
186// Simple Implementations for Testing
187// ============================================================================
188
189/// A simple UTXO for testing purposes.
190#[derive(Debug, Clone)]
191pub struct SimpleUtxo {
192 /// Satoshi value.
193 pub satoshis: u64,
194 /// Locking script.
195 pub locking_script: LockingScript,
196}
197
198impl TransactionOutputContext for SimpleUtxo {
199 fn satoshis(&self) -> u64 {
200 self.satoshis
201 }
202
203 fn locking_script(&self) -> &LockingScript {
204 &self.locking_script
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_simple_utxo() {
214 let utxo = SimpleUtxo {
215 satoshis: 100_000,
216 locking_script: LockingScript::from_asm("OP_DUP OP_HASH160").unwrap(),
217 };
218
219 assert_eq!(utxo.satoshis(), 100_000);
220 assert!(utxo.locking_script().to_asm().contains("OP_DUP"));
221 }
222}