use super::{LockingScript, UnlockingScript};
pub trait TransactionInputContext {
fn source_txid(&self) -> &[u8; 32];
fn source_output_index(&self) -> u32;
fn sequence(&self) -> u32;
fn source_satoshis(&self) -> Option<u64>;
fn source_locking_script(&self) -> Option<&LockingScript>;
fn unlocking_script(&self) -> &UnlockingScript;
}
pub trait TransactionOutputContext {
fn satoshis(&self) -> u64;
fn locking_script(&self) -> &LockingScript;
}
pub trait TransactionContext {
type Input: TransactionInputContext;
type Output: TransactionOutputContext;
fn version(&self) -> i32;
fn inputs(&self) -> &[Self::Input];
fn outputs(&self) -> &[Self::Output];
fn lock_time(&self) -> u32;
fn input(&self, index: usize) -> Option<&Self::Input> {
self.inputs().get(index)
}
fn output(&self, index: usize) -> Option<&Self::Output> {
self.outputs().get(index)
}
fn input_count(&self) -> usize {
self.inputs().len()
}
fn output_count(&self) -> usize {
self.outputs().len()
}
}
pub trait SpendValidation: TransactionContext {
fn validate_input(
&self,
index: usize,
) -> Result<bool, Box<crate::script::ScriptEvaluationError>>;
fn validate_all_inputs(&self) -> Result<(), Box<crate::script::ScriptEvaluationError>> {
for i in 0..self.input_count() {
match self.validate_input(i) {
Ok(true) => continue,
Ok(false) => {
return Err(Box::new(crate::script::ScriptEvaluationError {
message: format!("Input {} validation returned false", i),
source_txid: String::new(),
source_output_index: 0,
context: crate::script::ExecutionContext::UnlockingScript,
program_counter: 0,
stack: vec![],
alt_stack: vec![],
if_stack: vec![],
stack_mem: 0,
alt_stack_mem: 0,
}));
}
Err(e) => return Err(e),
}
}
Ok(())
}
}
pub trait UtxoProvider {
fn get_utxo(&self, txid: &[u8; 32], output_index: u32) -> Option<(u64, LockingScript)>;
}
#[derive(Debug, Clone)]
pub struct SimpleUtxo {
pub satoshis: u64,
pub locking_script: LockingScript,
}
impl TransactionOutputContext for SimpleUtxo {
fn satoshis(&self) -> u64 {
self.satoshis
}
fn locking_script(&self) -> &LockingScript {
&self.locking_script
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_utxo() {
let utxo = SimpleUtxo {
satoshis: 100_000,
locking_script: LockingScript::from_asm("OP_DUP OP_HASH160").unwrap(),
};
assert_eq!(utxo.satoshis(), 100_000);
assert!(utxo.locking_script().to_asm().contains("OP_DUP"));
}
}